<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  
  <title>Daidaini的 个人博客</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta property="og:type" content="website">
<meta property="og:title" content="Daidaini的 个人博客">
<meta property="og:url" content="https://daidaini.giteee.io/selfblog/index.html">
<meta property="og:site_name" content="Daidaini的 个人博客">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="Daidaini">
<meta name="twitter:card" content="summary">
  
    <link rel="alternate" href="/selfblog/atom.xml" title="Daidaini的 个人博客" type="application/atom+xml">
  
  
    <link rel="shortcut icon" href="/selfblog/favicon.png">
  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-source-code-pro@0.0.71/index.min.css">

  
  
<link rel="stylesheet" href="/selfblog/css/style.css">

  
    
<link rel="stylesheet" href="/selfblog/fancybox/jquery.fancybox.min.css">

  
<meta name="generator" content="Hexo 6.3.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/selfblog/" id="logo">Daidaini的 个人博客</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/selfblog/">Home</a>
        
          <a class="main-nav-link" href="/selfblog/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/selfblog/atom.xml" title="RSS 订阅"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="搜索"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="搜索"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="https://daidaini.giteee.io/selfblog"></form>
      </div>
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main">
  
    <article id="post-回调函数的理解" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/selfblog/2023/02/09/%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E7%9A%84%E7%90%86%E8%A7%A3/" class="article-date">
  <time class="dt-published" datetime="2023-02-09T02:30:40.000Z" itemprop="datePublished">2023-02-09</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/selfblog/categories/C-11/">C++11</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/selfblog/2023/02/09/%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E7%9A%84%E7%90%86%E8%A7%A3/">回调函数的理解</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <hr>
<h2 id="回调函数的基本定义"><a href="#回调函数的基本定义" class="headerlink" title="回调函数的基本定义"></a>回调函数的基本定义</h2><p>回调函数定义上来说，指的就是作为另外一个函数的参数的函数。</p>
<p>实现原理，可以简单概括为：</p>
<blockquote>
<p>存在一个函数，它的入参中有一个函数指针（能传入函数地址即可）。<br>顺序执行函数内部代码时，在适合的时候使用这个函数指针，以达到希望的结果。<br>（比如，传出某些计算出来的数据，或者生成某些需要给其他调用者的信息等）</p>
</blockquote>
<p>C语言中的回调函数大多都是 void*的形式存在，而void*指代的即是回调函数的函数指针。  </p>
<p>如下示例：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">Sum</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a+b;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//cbfunc，用以传入函数指针</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">SomeFunc</span><span class="params">(<span class="type">void</span>* cbfunc, <span class="type">int</span> param1, <span class="type">int</span> param2)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="type">int</span> result = <span class="built_in">cbfunc</span>(param1, param2);</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//调用</span></span><br><span class="line"><span class="built_in">SomeFunc</span>(Sum, <span class="number">2</span>, <span class="number">5</span>);</span><br></pre></td></tr></table></figure>

<hr>
<h2 id="回调函数的作用"><a href="#回调函数的作用" class="headerlink" title="回调函数的作用"></a>回调函数的作用</h2><h3 id="延迟执行"><a href="#延迟执行" class="headerlink" title="延迟执行"></a>延迟执行</h3><p>回调函数的这个用处，可以从上述代码中就能略知一二。<br>调用SomeFunc函数的时候传入了Sum方法，但是Sum方法的执行却需要在函数内部执行到对应代码的时候。</p>
<p>此外，我们平时使用的标准库的算法，大多也是处于这种目的来使用回调函数。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">std::vector&lt;std::string&gt; vec&#123;</span><br><span class="line">	<span class="string">&quot;abcdef&quot;</span>, <span class="string">&quot;123456&quot;</span>, <span class="string">&quot;abcdef123456&quot;</span>, <span class="string">&quot;123456abcdef&quot;</span>&#125;;</span><br><span class="line"></span><br><span class="line">std::for_each(vec.<span class="built_in">begin</span>(), vec.<span class="built_in">end</span>(),</span><br><span class="line">			  [](<span class="type">const</span> std::string &amp;str)</span><br><span class="line">			  &#123;</span><br><span class="line">				  <span class="keyword">if</span> (str.<span class="built_in">length</span>() &gt; <span class="number">10</span>)</span><br><span class="line">				  &#123;</span><br><span class="line">					  std::cout &lt;&lt; str &lt;&lt; <span class="string">&#x27;\n&#x27;</span>;</span><br><span class="line">				  &#125;</span><br><span class="line">			  &#125;);</span><br></pre></td></tr></table></figure>
<p>标准库的算法，大多都是类似上述的方法，通过Lambda函数作为回调函数的形式来完成使用。</p>
<hr>
<h3 id="分离调用者和被调用者"><a href="#分离调用者和被调用者" class="headerlink" title="分离调用者和被调用者"></a>分离调用者和被调用者</h3><p>回调函数可以使调用者不需要关心谁是被调用者，<br>只需要知道，存在一个具有某种特定原型、某些限制条件的被调用函数。</p>
<p>回调函数的这个作用，通常可用于不同模块之间的异步数据通知。  </p>
<p>而怎么使用回调函数达到这个设计，在C++中有多种模式，除了上边已经提到的函数指针和lambda函数，还可以使用函数模板std::funciton 或者纯虚抽象类的形式。</p>
<h4 id="std-function"><a href="#std-function" class="headerlink" title="std::function"></a>std::function</h4><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Callback</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">OnResponse</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *data, <span class="type">int</span> len)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;Calling OnResponse :&quot;</span>;</span><br><span class="line">        cout &lt;&lt; std::<span class="built_in">string</span>(data, len) &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">Callback</span>() = <span class="keyword">default</span>;</span><br><span class="line">    ~<span class="built_in">Callback</span>() = <span class="keyword">default</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> std::function&lt;<span class="type">void</span>(<span class="type">const</span> <span class="type">char</span> *, <span class="type">int</span>)&gt; OnRspFuncType;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Client</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">RegisterCallback</span><span class="params">(OnRspFuncType func)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        onRspFunc_ = std::<span class="built_in">move</span>(func);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">Running</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">milliseconds</span>(i));</span><br><span class="line">            <span class="keyword">if</span> (i % <span class="number">20</span> == <span class="number">5</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                std::string content = fmt::format(<span class="string">&quot;SendBack: &#123;&#125;&quot;</span>, i);</span><br><span class="line">                <span class="built_in">onRspFunc_</span>(content.<span class="built_in">data</span>(), (<span class="type">int</span>)content.<span class="built_in">length</span>());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    OnRspFuncType onRspFunc_;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>上述例子使用c++11的 std::function 来定义回调函数的类型。<br>std::function 配合 std::bind使用，可以达到回调函数的参数传递的目的。<br>相对直接使用函数指针，这么使用至少可以保证类型安全，且编译器的检查可以确保调用正确。  </p>
<p>使用上述回调类的代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Test</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Callback cb;</span><br><span class="line">    Client cli;</span><br><span class="line">    cli.<span class="built_in">RegisterCallback</span>(std::<span class="built_in">bind</span>(&amp;Callback::OnResponse, &amp;cb, placeholders::_1, placeholders::_2));</span><br><span class="line">    cli.<span class="built_in">Running</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h4 id="纯虚抽象类"><a href="#纯虚抽象类" class="headerlink" title="纯虚抽象类"></a>纯虚抽象类</h4><p>当然，除了上述的 函数指针、lambda函数、std::function这些，我们平时比较常见的使用回调函数的形式是纯虚的抽象类接口的形式。</p>
<p>举个例子来说明下纯虚类对回调函数的使用：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//纯虚抽象类定义</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CallbackInterface</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">OnResponse</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *data, <span class="type">int</span> len)</span></span>&#123;&#125;</span><br><span class="line">    <span class="keyword">virtual</span> ~<span class="built_in">CallbackInterface</span>()&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//被调用者继承抽象类，实现具体回调</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Callback</span> : <span class="keyword">public</span> CallbackInterface</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">OnResponse</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *data, <span class="type">int</span> len)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;Calling OnResponse :&quot;</span>;</span><br><span class="line">        cout &lt;&lt; std::<span class="built_in">string</span>(data, len) &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">Callback</span>() = <span class="keyword">default</span>;</span><br><span class="line">    ~<span class="built_in">Callback</span>() = <span class="keyword">default</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//调用者代码</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Client</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">RegisterCallback</span><span class="params">(CallbackInterface* cb)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        cb_ = cb;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//模拟使用回调函数</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">Running</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; ++i)</span><br><span class="line">        &#123;</span><br><span class="line">            this_thread::<span class="built_in">sleep_for</span>(chrono::<span class="built_in">milliseconds</span>(i));</span><br><span class="line">            <span class="keyword">if</span> (i % <span class="number">20</span> == <span class="number">5</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                std::string content = fmt::format(<span class="string">&quot;SendBack: &#123;&#125;&quot;</span>, i);</span><br><span class="line">                cb_-&gt;<span class="built_in">OnResponse</span>(content.<span class="built_in">data</span>(), (<span class="type">int</span>)content.<span class="built_in">length</span>()); <span class="comment">// error</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    CallbackInterface *cb_;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Test</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    Client cli;</span><br><span class="line">    CallbackInterface* cb = <span class="keyword">new</span> Callback;</span><br><span class="line">    cli.<span class="built_in">RegisterCallback</span>(cb);</span><br><span class="line">    cli.<span class="built_in">Running</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这里这个回调类只声明了一个回调函数，因此可能感觉使用纯虚类有一点迂回。<br>但如果是需要多个回调函数的场景，使用这种方式的话，就比较简便了，（因为注册的调用只需要一次即可）。  </p>
<hr>
<p>最后补充一点，C++里的纯虚函数，在Java里其实就是Interace，顾名思义，够明显了。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://daidaini.giteee.io/selfblog/2023/02/09/%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E7%9A%84%E7%90%86%E8%A7%A3/" data-id="cldwhn3v200017cs12z5mhtw8" data-title="回调函数的理解" class="article-share-link">分享</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/selfblog/tags/C-%E5%AD%A6%E4%B9%A0/" rel="tag">C++学习</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-二分法的理解" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/selfblog/2023/02/07/%E4%BA%8C%E5%88%86%E6%B3%95%E7%9A%84%E7%90%86%E8%A7%A3/" class="article-date">
  <time class="dt-published" datetime="2023-02-06T16:00:00.000Z" itemprop="datePublished">2023-02-07</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/selfblog/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/">数据结构与算法</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/selfblog/2023/02/07/%E4%BA%8C%E5%88%86%E6%B3%95%E7%9A%84%E7%90%86%E8%A7%A3/">二分法的理解</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h2 id="使用循环实现"><a href="#使用循环实现" class="headerlink" title="使用循环实现"></a>使用循环实现</h2><p>先看一道leetcode上的题：<br><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/">在排序数组中查找元素的第一个和最后一个位置</a></p>
<p>题目明确要求 实现的时间复杂度为 O(log n)。这个要求，很容易让我们联想到二分法这个算法。<br>再拆解下题目描述，已经是有序数组，满足二分查找的前提，那么只需要通过二分法找到目标元素的位置，再向前后迭代获取最左和最右的索引即是题目所需要的答案。</p>
<p>列一下所实现的二分查找的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//只返回查找到的位置，不确定是最左或最右</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">BinarySearch</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums, <span class="type">int</span> target)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="built_in">assert</span>(nums.<span class="built_in">size</span>() &gt;= <span class="number">2</span>);</span><br><span class="line">	<span class="type">int</span> left = <span class="number">0</span>;</span><br><span class="line">	<span class="type">int</span> right = nums.<span class="built_in">size</span>() - <span class="number">1</span>;</span><br><span class="line">	<span class="keyword">while</span> (left &lt;= right)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="type">int</span> mid = (right + left) / <span class="number">2</span>;</span><br><span class="line">		<span class="type">int</span> midval = nums[mid];</span><br><span class="line">		<span class="keyword">if</span> (midval &lt; target) <span class="comment">//往右折</span></span><br><span class="line">		&#123;</span><br><span class="line">			left = mid + <span class="number">1</span>;	<span class="comment">//由于left &lt;= mid ，所以要确保left右移需要加1</span></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span> <span class="keyword">if</span> (midval &gt; target) <span class="comment">//往左折</span></span><br><span class="line">		&#123;</span><br><span class="line">			right = mid - <span class="number">1</span>;	<span class="comment">//由于mid&lt;right, 必会使right左移，但这里mid已经检查过一遍，最差情况会导致再检查一次，</span></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span></span><br><span class="line">		&#123;</span><br><span class="line">			<span class="keyword">return</span> mid;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>代码理解:</p>
<ul>
<li>二分查找需要确定区间，每次折半，所以需要定义左右端点位置 left、right</li>
<li>在循环内逐步缩小区间，结束区间的条件定为 left&lt;&#x3D;right，意为在left&#x3D;&#x3D;right还要进行一次判断，该值是不是target目标值</li>
<li>折半查找，需要定义mid位置，mid &#x3D; (right+left)&#x2F;2，这样做的话，在left&lt;right时，必然会有mid&lt;right，mid&gt;&#x3D;left条件的成立</li>
<li>将mid位置的值与target进行比较，三种情况分别处理。<ul>
<li>相等则返回，表示已查找到</li>
<li>中值小于target值，表示target值在右区间，需要将left端点右移，left &#x3D; mid+1，需要加1，这样的最极端情况是left&#x3D;&#x3D;right，循环后再判断一次即可</li>
<li>中值大于target值，表示target值在左区间，需要将right端点左移，right &#x3D; mid-1，减1，这样的最极端情况是left&#x3D;&#x3D;mid的时候，这种情况的时候表示无法左移了，那么right&#x3D;&#x3D;mid-1&#x3D;&#x3D;left-1，会退出循环，也是不存在遗漏查找的情况</li>
<li>默认返回-1，表示查找不到；如果查找到，会直接在循环中就返回</li>
</ul>
</li>
</ul>
<p>值得注意的是，如果代码如上，那么mid &#x3D; (right+left)&#x2F;2 和 mid &#x3D; (right+left+1)&#x2F;2 两种情况的处理没有区别。  </p>
<h2 id="使用递归实现"><a href="#使用递归实现" class="headerlink" title="使用递归实现"></a>使用递归实现</h2><p>依然通过leetcode上一个题来举例：<br><a target="_blank" rel="noopener" href="https://leetcode.cn/problems/search-in-rotated-sorted-array-ii/">搜索旋转排序数组 II</a><br>旋转后的数组是没法保证折半之后左右区间都是肯定满足左区间的数小于等于右区间的数的<br>忽略一些细节的判断，我们可以简单的在折半之后两边都进行查找，这样的话，使用递归方法来实现会使代码简单直观很多。  </p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">BinarySearch</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums, <span class="type">int</span> target, <span class="type">int</span> left, <span class="type">int</span> right)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">if</span> (left &gt; right)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="type">int</span> mid = (left + right) / <span class="number">2</span>;</span><br><span class="line">	<span class="type">int</span> midval = nums[mid];</span><br><span class="line">	<span class="keyword">if</span> (midval == target)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="keyword">return</span> mid;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">	<span class="type">int</span> result = <span class="built_in">BinarySearch</span>(nums, target, mid + <span class="number">1</span>, right);</span><br><span class="line">	<span class="keyword">if</span> (result != <span class="number">-1</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="keyword">return</span> result;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> <span class="built_in">BinarySearch</span>(nums, target, left, mid - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后简但调用就可以达到查找的目的</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">search</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums, <span class="type">int</span> target)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">BinarySearch</span>(nums, target, <span class="number">0</span>, nums.<span class="built_in">size</span>() - <span class="number">1</span>) != <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>二分折半的思想是共通的，注意临界点的情况即可。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://daidaini.giteee.io/selfblog/2023/02/07/%E4%BA%8C%E5%88%86%E6%B3%95%E7%9A%84%E7%90%86%E8%A7%A3/" data-id="cldwhn3uw00007cs142oncjye" data-title="二分法的理解" class="article-share-link">分享</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/selfblog/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/" rel="tag">数据结构与算法</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-指针的理解" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/selfblog/2023/01/31/%E6%8C%87%E9%92%88%E7%9A%84%E7%90%86%E8%A7%A3/" class="article-date">
  <time class="dt-published" datetime="2023-01-30T16:00:00.000Z" itemprop="datePublished">2023-01-31</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/selfblog/categories/C-%E7%BC%96%E7%A8%8B/">C++编程</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/selfblog/2023/01/31/%E6%8C%87%E9%92%88%E7%9A%84%E7%90%86%E8%A7%A3/">C指针的理解</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <p>简单的指针的声明一般有如下形式</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>* ptr = new <span class="type">int</span>;</span><br><span class="line"><span class="type">int</span> data = <span class="number">100</span>;</span><br><span class="line"><span class="type">int</span>* iptr = &amp;data;</span><br></pre></td></tr></table></figure>

<p>以下尝试使用c++的思想来理解下c指针的相关内容。<br>int* 这种可以理解为是整型指针类型，那么iptr就是整型指针类型的变量，iptr本身是左值，能取到地址，因此二级指针也就存在意义:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>** iiptr = &amp;iptr;</span><br></pre></td></tr></table></figure>
<p>指针类型的变量是用来存储地址数据的，因此一般是整型的，什么长度的整型跟机器是32位还是64位关联。指针变量的值对应的地址则是用来表示存储的数据的。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">*ptr = <span class="number">101</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, **iiptr);</span><br><span class="line">**iiptr = <span class="number">111</span>;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, *iptr);</span><br><span class="line">assert(*iptr == **iiptr);</span><br><span class="line">assert(&amp;iptr == iiptr);</span><br></pre></td></tr></table></figure>
<p>所以：</p>
<ul>
<li>一级指针的值是地址，就是表示存储数据的的变量的地址</li>
<li>二级指针的值也是地址，就是表示一级指针的这个变量的地址</li>
</ul>
<p>那同样的思路，对于<strong>指针数组</strong>，可以写如下代码</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span>* cptr = new <span class="type">char</span>[<span class="number">256</span>];</span><br><span class="line"><span class="built_in">strcpy</span>(cptr, <span class="string">&quot;abcdefg&quot;</span>);</span><br><span class="line"><span class="type">char</span>** ccptr = &amp;cptr;</span><br><span class="line">assert(*cptr, <span class="string">&#x27;a&#x27;</span>);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%c, %c, %c\n&quot;</span>, **ccptr, *cptr + <span class="number">1</span>, **ccptr + <span class="number">2</span>);</span><br></pre></td></tr></table></figure>

<p>接着是对<strong>函数指针</strong>的理解。<br>函数指针一般以这种方式声明：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="title function_">int</span> <span class="params">(*funcptr)</span><span class="params">(<span class="type">int</span>, <span class="type">int</span>)</span>;</span><br></pre></td></tr></table></figure>
<p>如果有以下满足函数指针声明类型的函数</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">Add</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> <span class="title function_">Sub</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a -b;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>简单的使用函数指针来表示函数如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">funcptr somefunc = Add;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%p\n&quot;</span>, somefunc);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, somefunc(<span class="number">20</span>, <span class="number">10</span>));</span><br><span class="line">somefunc = Sub;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%p\n&quot;</span>, somefunc);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, somefunc(<span class="number">20</span>, <span class="number">10</span>));</span><br></pre></td></tr></table></figure>
<p>这里的函数指针类型变量somefunc的值就是要表示的函数的地址。<br>既然somefunc是表示值的变量，即使它是指针，它也可以取到地址，那就可以使用二级指针来表示这个指针类型变量的地址。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">decltype(&amp;somefunc) ptr_somefunc = &amp;somefunc;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%p\n&quot;</span>, ptr_somefunc);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, (*ptr_somefunc)(<span class="number">30</span>, <span class="number">10</span>));</span><br><span class="line">*ptr_somefunc = Add;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%p\n&quot;</span>, ptr_somefunc);</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, (*ptr_somefunc)(<span class="number">30</span>, <span class="number">10</span>));</span><br></pre></td></tr></table></figure>
<p>这里使用decltype只是为了简化代码，省略一个二级函数指针的定义，否则需要：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="title function_">int</span> <span class="params">(**pfuncptr)</span><span class="params">(<span class="type">int</span>, <span class="type">int</span>)</span>;</span><br><span class="line">pfuncptr ptr_somefunc = &amp;somefunc;</span><br></pre></td></tr></table></figure>
<p>此外，可以确认两次打印二级函数指针的地址是一样的，因为它表示的是ptr_somefunc变量的地址。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://daidaini.giteee.io/selfblog/2023/01/31/%E6%8C%87%E9%92%88%E7%9A%84%E7%90%86%E8%A7%A3/" data-id="cldmi24ly000dyhs186aqfigs" data-title="C指针的理解" class="article-share-link">分享</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/selfblog/tags/C%E8%AF%AD%E8%A8%80/" rel="tag">C语言</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-理解CPP的移动语义" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/selfblog/2023/01/31/%E7%90%86%E8%A7%A3CPP%E7%9A%84%E7%A7%BB%E5%8A%A8%E8%AF%AD%E4%B9%89/" class="article-date">
  <time class="dt-published" datetime="2023-01-30T16:00:00.000Z" itemprop="datePublished">2023-01-31</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/selfblog/categories/C-%E7%BC%96%E7%A8%8B/">C++编程</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/selfblog/2023/01/31/%E7%90%86%E8%A7%A3CPP%E7%9A%84%E7%A7%BB%E5%8A%A8%E8%AF%AD%E4%B9%89/">理解CPP的移动语义</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h2 id="什么是移动语义"><a href="#什么是移动语义" class="headerlink" title="什么是移动语义"></a>什么是移动语义</h2><p>移动语义是从C++11标准开始支持的，那什么是移动语义呢？</p>
<p>先引用下《Effective Modern C++》中关于移动语义的说明</p>
<blockquote>
<p>Move semantics makes it possible for compilers to replace expensive copying operations with less expensive moves. In the same way that copy constructors and copy assignment operators give you control over what it means to copy objects, move constructors and move assignment operators offer control over the semantics of moving. Move semantics also enables the creation of move-only types, such as std::unique_ptr, std::future, and std::thread.</p>
</blockquote>
<p>提取下关键信息就是：移动语义让编译器在需要拷贝对象时可以使用一种代价更低的移动的方式来执行。拷贝对象是通过拷贝构造和拷贝赋值，移动对象是通过移动构造和移动赋值。此外，移动语义还可以处理只能移动的对象的构造。</p>
<p>以下为个人理解：<br>移动语义的直接作用对象是函数的参数，发生移动的最直接的场景就是对自定义类型的参数进行传递的时候。<br>当参数是作为右值形式进行传递时，会调用该类型的移动构造函数(或移动赋值)以构造临时对象，举例说明。  </p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">  <span class="built_in">A</span>() = <span class="keyword">default</span>;</span><br><span class="line">  <span class="built_in">A</span>(A&amp;&amp; rhs) <span class="keyword">noexcept</span> &#123;</span><br><span class="line">			cout &lt;&lt; <span class="string">&quot;using move construct.\n&quot;</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	A&amp; <span class="keyword">operator</span>=(A&amp;&amp; rhs) <span class="keyword">noexcept</span> &#123;</span><br><span class="line">		cout &lt;&lt; <span class="string">&quot;using move assign.\n&quot;</span>;</span><br><span class="line">		<span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(A param)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	cout &lt;&lt; <span class="string">&quot;using func.\n&quot;</span>;</span><br><span class="line">  A a1;</span><br><span class="line">	a1 = std::<span class="built_in">move</span>(param);  <span class="comment">//调用移动赋值</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">test</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	A a;</span><br><span class="line">	<span class="comment">// func(a); //error. </span></span><br><span class="line">	<span class="built_in">func</span>(std::<span class="built_in">move</span>(a)); <span class="comment">//通过移动构造构建临时对象</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>输出如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">using move construct.</span><br><span class="line">using func...</span><br><span class="line">using move assign.</span><br></pre></td></tr></table></figure>
<p>例子解释：</p>
<ul>
<li>A(A&amp;&amp; rhs) 这是移动构造函数的声明方式</li>
<li>A&amp; operator&#x3D;(A&amp;&amp; rhs) 这是移动赋值函数的声明方式</li>
<li>std::move(param) 使用std::move可以将参数强制转换为右值形式。</li>
<li>func函数是使用值传递的方式，因此传参时会构造临时对象，由于参数转为了右值类型，因此会调用移动构造方法(打印了using move construct)</li>
<li>不能直接使用func(a)，因为在显示声明了移动构造函数后，编译器就不会自动生成拷贝构造函数了</li>
</ul>
<p>可见，虽然上述例子是最简单的使用移动语义的场景，但依然会不可避免的涉及到几个概念的理解：</p>
<ol>
<li><strong>左值和右值怎么区分</strong></li>
<li><strong>移动构造函数以及移动赋值函数</strong></li>
<li><strong>std::move强转右值，std::forward完美转发</strong></li>
</ol>
<p>我们可能还会考虑一个问题，<strong>函数传参除了值传递，还有引用传递，引用传递有单引用也有双引用，除了引用传递，此外还有使用指针传递，这些传递方式在移动语义的表现上都有什么区别呢？</strong></p>
<hr>
<h2 id="移动拷贝和移动赋值"><a href="#移动拷贝和移动赋值" class="headerlink" title="移动拷贝和移动赋值"></a>移动拷贝和移动赋值</h2><p>对于移动拷贝函数和移动赋值函数，其实上边的例子已经有简单的说明，其形式就是：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">A</span>(A&amp;&amp; rhs)</span><br><span class="line">A&amp; <span class="keyword">operator</span>=(A&amp;&amp; rhs)</span><br></pre></td></tr></table></figure>
<p>移动和拷贝的最大区别在于实现，拷贝要么就是深拷贝要么就是浅拷贝，而移动就真的就是移动掉了，不存在拷贝。它们的开销代价也因此而来。<br>举一个例子说明：</p>
<p>写一个MyString类，为简单阐述，只实现了拷贝赋值和移动赋值</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyString</span> <span class="keyword">final</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="built_in">MyString</span>() &#123;</span><br><span class="line">		data_ = <span class="keyword">new</span> <span class="type">char</span>[<span class="number">1024</span>];</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	~<span class="built_in">MyString</span>() &#123;</span><br><span class="line">		<span class="keyword">if</span> (data_ != <span class="literal">nullptr</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			<span class="keyword">delete</span> data_;</span><br><span class="line">			data_ = <span class="literal">nullptr</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">  MyString&amp; <span class="keyword">operator</span>=(<span class="type">const</span> MyString&amp; rhs)&#123;</span><br><span class="line">		<span class="keyword">if</span> (rhs.data_ == <span class="literal">nullptr</span> || data_ == <span class="literal">nullptr</span>)</span><br><span class="line">			<span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line"></span><br><span class="line">		cout &lt;&lt; <span class="string">&quot;copy assigning..\n&quot;</span>;</span><br><span class="line">		::<span class="built_in">memcpy</span>(<span class="keyword">this</span>-&gt;data_, rhs.data_, <span class="number">1024</span>);</span><br><span class="line">		<span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  MyString&amp; <span class="keyword">operator</span>=(MyString&amp;&amp; rhs) <span class="keyword">noexcept</span> &#123;</span><br><span class="line">		<span class="keyword">if</span> (rhs.data_ == <span class="literal">nullptr</span> || data_ == <span class="literal">nullptr</span>)</span><br><span class="line">			<span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line"></span><br><span class="line">		cout &lt;&lt; <span class="string">&quot;move assigning..\n&quot;</span>;</span><br><span class="line">		data_ = rhs.data_;</span><br><span class="line">		rhs.data_ = <span class="literal">nullptr</span>;</span><br><span class="line">		<span class="keyword">return</span> *<span class="keyword">this</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="type">bool</span> <span class="title">Empty</span><span class="params">()</span> <span class="type">const</span></span>&#123;</span><br><span class="line">		<span class="keyword">return</span> data_ == <span class="literal">nullptr</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">  <span class="type">char</span>* data_ = <span class="literal">nullptr</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>这个类，目的是在拷贝赋值时是深拷贝，在移动赋值时仅仅是移动，通过测试函数进行验证</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Test</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  MyString s1;</span><br><span class="line">  MyString s2;</span><br><span class="line">  s1 = s2;</span><br><span class="line">  <span class="built_in">assert</span>(!s2.<span class="built_in">Empty</span>());</span><br><span class="line">  s1 = std::<span class="built_in">move</span>(s2); </span><br><span class="line">  <span class="built_in">assert</span>(s2.<span class="built_in">Empty</span>());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>输出结果</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">copy assigning..</span><br><span class="line">move assigning..</span><br></pre></td></tr></table></figure>

<hr>
<h2 id="左值右值"><a href="#左值右值" class="headerlink" title="左值右值"></a>左值右值</h2><h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><p>左值右值的概念可以总结概括为：</p>
<ul>
<li>左值指表达式结束后依然存在的持久对象，可以取到地址，比如具名变量或者对象实例</li>
<li>右值是表达式结束后就不再存在的临时对象，不可以取到地址，没有名字</li>
</ul>
<p>所以区分是左值还是右值，一个最有用的步骤即使看能否取到它的地址，如果可以就是左值，不可以则通常是一个右值。<br>此外，概念上来说，右值通常就是从方法中返回的临时对象（非绝对），而左值对应的就是实际可以指向的对象，不管是通过指针还是引用的形式。</p>
<p>右值还可以细分为纯右值(pure rvalue)和将亡值(expiring value)。</p>
<ul>
<li>纯右值：<ul>
<li>非引用返回的临时变量 </li>
<li>运算表达式产生的临时变量</li>
<li>原始字面量</li>
<li>lambda表达式</li>
</ul>
</li>
<li>将亡值：可以理解为将要被销毁，但是可以被移动的值</li>
</ul>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(std::vector&lt;<span class="type">int</span>&gt;&amp; vec)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="comment">//此处expiring就是一个将亡值</span></span><br><span class="line">  <span class="comment">//因为std::string 对象可以移动，因此最后会调用移动赋值函数将expiring的值移动到str中</span></span><br><span class="line">  std::vector&lt;<span class="type">int</span>&gt; expiring&#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line">  vec = std::<span class="built_in">move</span>(expiring);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="右值引用"><a href="#右值引用" class="headerlink" title="右值引用"></a>右值引用</h3><p>使用符号&amp;&amp; 表示右值引用。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">100</span>;</span><br><span class="line"><span class="type">int</span> &amp;&amp;r1 = std::<span class="built_in">move</span>(a);</span><br><span class="line"><span class="comment">//int &amp;&amp;r1 = a; //error! </span></span><br></pre></td></tr></table></figure>
<p>但是使用带&amp;&amp;的类型表示的变量，不一定就是右值，按照左值的定义，只要具名的能取到地址的变量就是左值，所以即使你使用&amp;&amp;来声明，该是左值的还是左值。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(<span class="type">int</span>&amp;&amp; param)</span> </span>&#123;</span><br><span class="line">	<span class="comment">//int&amp;&amp; r1 = param; //error! param是左值!</span></span><br><span class="line">	<span class="type">int</span>&amp;&amp; r1 = std::<span class="built_in">move</span>(param);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>此外，有一种特殊情况，《Effective Modern C++》中将其称为 **universal references(通用引用)**，<br>看几个示例:</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(std::vector&lt;<span class="type">int</span>&gt;&amp;&amp; param)</span></span>; <span class="comment">//右值引用</span></span><br><span class="line"></span><br><span class="line">std::vector&lt;<span class="type">int</span>&gt;&amp;&amp; vec1 = std::vector&lt;<span class="type">int</span>&gt;&#123;&#125;; <span class="comment">//右值引用</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span>&amp;&amp; vec2 = vec1;   <span class="comment">//非右值引用</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(std::vector&lt;T&gt;&amp;&amp; param)</span></span>;  <span class="comment">//右值引用，不是T&amp;&amp;的形式，不是universal reference</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(T&amp;&amp; param)</span>    <span class="comment">//非右值引用</span></span></span><br></pre></td></tr></table></figure>
<p>首先，universal reference必须是 T&amp;&amp; 的形式，且必须存在类型推导。<br>以上两个非右值引用的场景是最典型的两个universal references的场景。  </p>
<p>至于universal reference怎么判断是左值引用还是右值引用，需要进一步看它的构造者(即实际的传参)，如下示例： </p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">func</span><span class="params">(T&amp;&amp; param)</span></span>; <span class="comment">//param是一个 universal reference</span></span><br><span class="line"></span><br><span class="line">std::vector&lt;<span class="type">int</span>&gt; vec;</span><br><span class="line"><span class="built_in">func</span>(vec);    <span class="comment">//传参是左值，所以T&amp;&amp;对应的是左值引用 std::vector&lt;int&gt;&amp;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">func</span>(std::<span class="built_in">move</span>(vec)); <span class="comment">//传参是右值，所以param的类型是 std::vector&lt;int&gt;&amp;&amp;</span></span><br></pre></td></tr></table></figure>

<hr>
<h2 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h2><p>先明确，对于性能而言，纯C指针的性能肯定是没话说的，即使移动语义可以减少一些消耗，但还是不能完全达到C指针直接操作内存的效率。<br>但也需要明确，C指针的不安全性，是很多复杂问题的根源，标准库引入了各种智能指针，其实就是在提供方法尽量避免使用裸C指针。<br>移动语义的引入，在我看来是想在安全性和性能之间找到一种尽可能的平衡。</p>
<p>区分了左值右值，也知道了移动构造和移动赋值的作用，那移动语义我们平时怎么去应用呢？<br>其实，现在的标准库中，基本上所有的容器都已经了支持移动语义，再配合现代编译器普遍支持的RVO机制，我们可以更简洁明确的编写代码，而不用使用指针或者左值引用来既表示出参，又表示入参。</p>
<h3 id="一个简单的队列的示例"><a href="#一个简单的队列的示例" class="headerlink" title="一个简单的队列的示例"></a>一个简单的队列的示例</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">DemoQueue</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="function"><span class="type">void</span> <span class="title">Insert</span><span class="params">(T&amp;&amp; task)</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		tasks_.<span class="built_in">push</span>(std::<span class="built_in">move</span>(task));	</span><br><span class="line">		<span class="comment">//T&amp;&amp; task 模板展开后就不再需要自动推导，所以不是universal reference</span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function">T <span class="title">Get</span><span class="params">()</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		T t = std::<span class="built_in">move</span>(tasks_.<span class="built_in">front</span>()); <span class="comment">//front()返回的是左值引用，由于该元素后续会被移除(pop)，属于将亡值</span></span><br><span class="line">		tasks_.<span class="built_in">pop</span>();</span><br><span class="line">		<span class="keyword">return</span> t;  <span class="comment">//RVO 返回时不会再拷贝临时对象</span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">	std::queue&lt;T&gt; tasks_;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Test</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	TaskQueue&lt;std::string&gt; q1;</span><br><span class="line">	q1.<span class="built_in">Insert</span>(<span class="string">&quot;first&quot;</span>s);</span><br><span class="line">	q1.<span class="built_in">Insert</span>(<span class="string">&quot;second&quot;</span>s);	</span><br><span class="line">	q1.<span class="built_in">Insert</span>(<span class="string">&quot;third&quot;</span>s);</span><br><span class="line"></span><br><span class="line">	<span class="built_in">assert</span>(q1.<span class="built_in">Get</span>() == <span class="string">&quot;first&quot;</span>);</span><br><span class="line">	<span class="built_in">assert</span>(q1.<span class="built_in">Get</span>() == <span class="string">&quot;second&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="完美转发和通用引用"><a href="#完美转发和通用引用" class="headerlink" title="完美转发和通用引用"></a>完美转发和通用引用</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> T&gt;</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintMerge</span><span class="params">(T&amp;&amp; a, T&amp;&amp; b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	cout &lt;&lt; std::forward&lt;T&gt;(a) + std::forward&lt;T&gt;(b) &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">Test</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="type">int</span> a = <span class="number">10</span>, b = <span class="number">20</span>;</span><br><span class="line">	<span class="built_in">PrintMerge</span>(a, b);		<span class="comment">//这里传参为左值</span></span><br><span class="line">	std::string s1&#123; <span class="string">&quot;one &quot;</span> &#125;;</span><br><span class="line">	std::string s2&#123; <span class="string">&quot;two &quot;</span> &#125;;</span><br><span class="line">	<span class="built_in">PrintMerge</span>(std::<span class="built_in">move</span>(s1), std::<span class="built_in">move</span>(s2));	<span class="comment">//这里传参为右值</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>概括言之，std::forward完美转发的作用，就是把参数的实际类型左值还是右值引用继续传递下去。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://daidaini.giteee.io/selfblog/2023/01/31/%E7%90%86%E8%A7%A3CPP%E7%9A%84%E7%A7%BB%E5%8A%A8%E8%AF%AD%E4%B9%89/" data-id="cldmi24m0000eyhs18kes6bjg" data-title="理解CPP的移动语义" class="article-share-link">分享</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/selfblog/tags/C-%E7%89%B9%E6%80%A7/" rel="tag">C++特性</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-常用Linux命令" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/selfblog/2022/03/23/%E5%B8%B8%E7%94%A8Linux%E5%91%BD%E4%BB%A4/" class="article-date">
  <time class="dt-published" datetime="2022-03-23T13:14:28.000Z" itemprop="datePublished">2022-03-23</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/selfblog/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/">操作系统</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/selfblog/2022/03/23/%E5%B8%B8%E7%94%A8Linux%E5%91%BD%E4%BB%A4/">常用Linux命令</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h2 id="文件操作相关"><a href="#文件操作相关" class="headerlink" title="文件操作相关"></a>文件操作相关</h2><hr>
<h3 id="chmod"><a href="#chmod" class="headerlink" title="chmod"></a>chmod</h3><p>chmod有两种操作方式,<br>一种为普通授权法：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">chmod +x a.txt  </span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">给a.txt可执行的权限</span></span><br><span class="line">chmod +r a.txt  </span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">给a.txt可读的权限</span></span><br></pre></td></tr></table></figure>

<p>文件权限，’r’ 代表可读(4)，’w’代表可写(2)， ‘x’代表可执行(1)</p>
<p>上面括号内的数字 代表 “8421法”，即第二种操作方式。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">chmod 777 a.txt  </span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">表示给a.txt所有权限</span></span><br></pre></td></tr></table></figure>

<p>关于文件的权限信息说明：</p>
<blockquote>
<p>-r-xr-xr-x 1 yubo yubo 342 May 27 22:06 hello.sh*</p>
</blockquote>
<p>前面的 “-r-xr-xr-x” 即对应文件的权限信息。</p>
<p>其中，第一位是’-‘ 这个表示文件， 如果是’d’就表示文件夹；</p>
<p>后面分为三组，分别表示 “拥有者的权限”，“拥有者所在的组、组员的权限”，“其他用户的权限”；</p>
<p>每一组的权限，都是三位，分别代表 读写和执行的权限，所以，如果三个权限都有，就稳定为 “rwx”。</p>
<p>对上述文件，修改，使文件所有者有 所有权限：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">chmod 755 hello.sh</span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">ll</span></span><br><span class="line"><span class="meta prompt_">$</span><span class="language-bash">-rwxr-xr-x 1 yubo yubo  342 May 27 22:06 hello.sh*</span></span><br></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/205217949">[Linux]创建新用户及用户权限 - 知乎 (zhihu.com)</a></p>
<h3 id="sudo"><a href="#sudo" class="headerlink" title="sudo"></a>sudo</h3><p>sudo 命令 以系统管理者的身份执行，也就是说，经由sudo执行的命令就好像 是 root亲自执行。<br>需要输入自己的账户密码。<br>使用权限: 在 &#x2F;etc&#x2F;sudoers 中出现的使用者</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">以yao用户身份编辑 home目录下的index.html文件</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo -u yao vi ~www/index.html</span>  </span><br></pre></td></tr></table></figure>

<h3 id="编辑文件"><a href="#编辑文件" class="headerlink" title="编辑文件"></a>编辑文件</h3><p><strong>命令: vi 和 vim</strong></p>
<p>操作： vi + 文件名</p>
<p>进入后，操作界面有三种模式：命令模式、插入模式和底行模式</p>
<p>三种模式相关定义：</p>
<p><strong>命令模式</strong></p>
<ul>
<li><p>刚进入文件就是命令模式，通过方向键控制光标位置，</p>
</li>
<li><p>使用命令”dd”删除当前整行</p>
</li>
<li><p>使用命令”&#x2F;字段”进行查找</p>
</li>
<li><p>按”i”在光标所在字符前开始插入</p>
</li>
<li><p>按”a”在光标所在字符后开始插入</p>
</li>
<li><p>按”o”在光标所在行的下面另起一新行插入</p>
</li>
<li><p>按”：”进入底行模式</p>
</li>
</ul>
<p><strong>插入模式</strong></p>
<ul>
<li><p>此时可以对文件内容进行编辑，左下角会显示 “– 插入 –””</p>
</li>
<li><p>按”ESC”进入底行模式</p>
</li>
<li><p>底行模式</p>
</li>
<li><p>退出编辑 :q</p>
</li>
<li><p>强制退出 :q!</p>
</li>
<li><p>保存并退出 :wq</p>
</li>
</ul>
<p><strong>操作步骤示例</strong></p>
<ol>
<li><p>保存并退出编辑  “ESC” -&gt; 输入”:” -&gt; 输入”wq”,回车</p>
</li>
<li><p>取消操作：按”ESC” -&gt; 输入”:” -&gt; 输入”q!”,回车</p>
</li>
</ol>
<p><strong>补充</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">vim +10 filename.txt </span><br><span class="line">#打开文件并跳到第10行</span><br><span class="line">vim -R /etc/passwd </span><br><span class="line">#以只读模式打开文件</span><br></pre></td></tr></table></figure>

<h3 id="文件创建查看删除"><a href="#文件创建查看删除" class="headerlink" title="文件创建查看删除"></a>文件创建查看删除</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">创建文件</span></span><br><span class="line">touch a.txt</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">移动文件</span></span><br><span class="line">mv [选项] 文件名 [目标文件名|目标路径]</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">复制文件</span></span><br><span class="line">cp [选项] 源文件 目标文件</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">复制文件夹</span></span><br><span class="line">cp -r dir1 dir2</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">删文件 -f 忽略警告信息 -i 删除前先询问</span></span><br><span class="line">rm [选项] 文件</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">删除文件夹</span> </span><br><span class="line">rm -r 目录名</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">重命名</span></span><br><span class="line">rename libfmt.a libfmt.a.bak libfmt.a</span><br></pre></td></tr></table></figure>

<h3 id="diff"><a href="#diff" class="headerlink" title="diff"></a>diff</h3><p>显示两个文件的差异  </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">diff -c file1 file2</span><br></pre></td></tr></table></figure>

<h3 id="压缩-x2F-解压文件"><a href="#压缩-x2F-解压文件" class="headerlink" title="压缩&#x2F;解压文件"></a>压缩&#x2F;解压文件</h3><blockquote>
<p>tar[选项][文件]</p>
</blockquote>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">打包并压缩文件，压缩包后缀为 .tar.gz</span></span><br><span class="line">tar -czvf *.tar.gz</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">解压并展开压缩包，压缩包后缀为 .tar.gz</span></span><br><span class="line">tar -xzvf *.tar.gz</span><br></pre></td></tr></table></figure>

<p><strong>具体参数参考如下：</strong></p>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>参数</td>
<td>作用</td>
</tr>
<tr>
<td>-c</td>
<td>创建压缩文件</td>
</tr>
<tr>
<td>-x</td>
<td>解开压缩文件</td>
</tr>
<tr>
<td>-t</td>
<td>查看压缩包内有哪些文件</td>
</tr>
<tr>
<td>-z</td>
<td>用Gzip压缩或者解压</td>
</tr>
<tr>
<td>-j</td>
<td>用bip2压缩或者解压</td>
</tr>
<tr>
<td>-v</td>
<td>显示压缩或者解压的过程</td>
</tr>
<tr>
<td>-f</td>
<td>目标文件名</td>
</tr>
<tr>
<td>-p</td>
<td>保留原始的权限和属性</td>
</tr>
<tr>
<td>-P</td>
<td>使用绝对路径来压缩</td>
</tr>
<tr>
<td>-C</td>
<td>指定解压到的目录</td>
</tr>
</tbody></table>
<hr>
<h2 id="查找文件"><a href="#查找文件" class="headerlink" title="查找文件"></a>查找文件</h2><h3 id="find"><a href="#find" class="headerlink" title="find"></a>find</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">find /bin -name &#x27;a*&#x27; </span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">查找bin目录下所有以a开头的文件或者目录</span></span><br><span class="line"></span><br><span class="line">find . -name &#x27;*.c&#x27; </span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">将当前目录及其子目录下，所有.c文件找出来</span></span><br><span class="line"></span><br><span class="line">find . -type f</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">将当前目录及其子目录下的所有一般文件列出</span></span><br><span class="line"></span><br><span class="line">find . -ctime -20</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">将当前目录及其子目录下 所有最近20天更新过的文件列出</span></span><br><span class="line"></span><br><span class="line">find . -type f -perm 664 -exec ls -l &#123;&#125; \;</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">将当前目录及其子目录下， 所有权限为664的文件，列出</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">解释：这个命令可以分为两部分看，前半部分，到 -perm 664表示是查看文件的信息的权限；</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">后半部分，表示从执行<span class="built_in">ls</span> -l 命令获取到的信息中进行筛选。</span></span><br></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="https://note.youdao.com/s/RlB4GHsJ">find工具笔记</a></p>
<h3 id="whereis"><a href="#whereis" class="headerlink" title="whereis"></a>whereis</h3><p>将和ls命令相关的文件都找出来  参数是某个命令（验证过 cmake ssh等）</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">whereis ls</span><br></pre></td></tr></table></figure>

<h3 id="which"><a href="#which" class="headerlink" title="which"></a>which</h3><p>which指令 会在环境变量 $PATH 设置的目录里查找符合条件的文件</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">which is bash</span><br></pre></td></tr></table></figure>

<h3 id="grep"><a href="#grep" class="headerlink" title="grep"></a>grep</h3><blockquote>
<p>grep [选项] [文件]</p>
</blockquote>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">在文件中查找字符串（不区分大小写）</span></span><br><span class="line">grep -i &quot;the&quot; demo_file  </span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">在一个文件夹中递归查询包括指定字符串的文件</span></span><br><span class="line">grep -r &quot;remesh&quot; *  </span><br></pre></td></tr></table></figure>

<table>
<thead>
<tr>
<th>参数</th>
<th>作用</th>
</tr>
</thead>
<tbody><tr>
<td>-b</td>
<td>将可执行文件(binary) 当作文本文件(txt)来搜索</td>
</tr>
<tr>
<td>-c</td>
<td>仅显示查找到的次数</td>
</tr>
<tr>
<td>-i</td>
<td>忽略大小写</td>
</tr>
<tr>
<td>-n</td>
<td>显示行号</td>
</tr>
<tr>
<td>-v</td>
<td>反向选择–仅列出没有“关键词”的行</td>
</tr>
</tbody></table>
<hr>
<h2 id="下载文件"><a href="#下载文件" class="headerlink" title="下载文件"></a>下载文件</h2><h3 id="wget"><a href="#wget" class="headerlink" title="wget"></a>wget</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">该命令用于从网上下载内容</span></span><br><span class="line">wget http://prdownloads.sourceforge.net/sourceforge/nagios/nagios-3.2.1.tar.gz</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">下载文件并以指定的文件名保存文件</span></span><br><span class="line">wget -O nagios.tar.gz http://prdownloads.sourceforge.net/sourceforge/nagios/nagios-3.2.1.tar.gz</span><br></pre></td></tr></table></figure>

<h3 id="ftp"><a href="#ftp" class="headerlink" title="ftp"></a>ftp</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ftp IP/hostname //访问ftp服务器</span><br><span class="line">mls *.html - //显示远程主机上文件列表</span><br></pre></td></tr></table></figure>

<h3 id="scp"><a href="#scp" class="headerlink" title="scp"></a>scp</h3><p>通过scp命令在多台服务器中相互复制、传输文件(secure copy的缩写)<br>scp 是linux系统下基于ssh登录进行安全的远程文件拷贝命令<br>scp是加密的， rcp是不加密的， scp是rcp的加强版本</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">scp /opt/data.txt  192.168.1.101:/opt/    </span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">将本地opt目录下的data文件发送到192.168.1.101服务器的opt目录下</span></span><br></pre></td></tr></table></figure>

<hr>
<h2 id="网络操作"><a href="#网络操作" class="headerlink" title="网络操作"></a>网络操作</h2><h3 id="防火墙操作"><a href="#防火墙操作" class="headerlink" title="防火墙操作"></a>防火墙操作</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">service iptables status      //查看iptables服务的状态</span><br><span class="line">service iptables start       //开启iptables服务</span><br><span class="line">service iptables stop        //停止iptables服务</span><br><span class="line">service iptables restart     //重启iptables服务</span><br><span class="line">chkconfig iptables off       //关闭iptables服务的开机自启动</span><br><span class="line">chkconfig iptables on        //开启iptables服务的开机自启动</span><br></pre></td></tr></table></figure>


<h3 id="查看网络信息"><a href="#查看网络信息" class="headerlink" title="查看网络信息"></a>查看网络信息</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">ifconfig</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">查看与IP的连接情况</span></span><br><span class="line">ping IP </span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">查看当前系统端口</span></span><br><span class="line">netstat -an  </span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">查看指定端口</span></span><br><span class="line">netstat -an | grep 8080  </span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">远程主机，需要输入用户名密码</span></span><br><span class="line">ssh IP  </span><br></pre></td></tr></table></figure>

<h3 id="lsof"><a href="#lsof" class="headerlink" title="lsof"></a>lsof</h3><p>列出当前系统打开的文件描述符（list openfiles）</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">仅显示TCP连接（同理UDP）</span></span><br><span class="line">lsof -iTCP</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">显示与指定端口相关的网络信息</span></span><br><span class="line">lsof -i:3322</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">显示指定到指定主机的连接</span></span><br><span class="line">lsof -i@192.168.6.25</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">加端口</span></span><br><span class="line">lsof -i@192.168.6.25:23386</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">找出监听端口</span></span><br><span class="line">lsof -i -sTCP:LISTEN</span><br><span class="line">lsof -i | grep -i LISTEN</span><br></pre></td></tr></table></figure>
<p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/450234442">Linux 命令神器：lsof</a></p>
<h3 id="修改IP"><a href="#修改IP" class="headerlink" title="修改IP"></a>修改IP</h3><p>修改网络配置文件，文件地址为</p>
<blockquote>
<p>&#x2F;etc&#x2F;sysconfig&#x2F;network-scripts&#x2F;ifcfg-eth0</p>
</blockquote>
<p><strong>主要对应以下配置：</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">TYPE=Ethernet               //网络类型</span><br><span class="line">BOOTPROTO=static            //静态IP</span><br><span class="line">DEVICE=ens00                //网卡名</span><br><span class="line">IPADDR=192.168.1.100        //设置的IP</span><br><span class="line">NETMASK=255.255.255.0       //子网掩码</span><br><span class="line">GATEWAY=192.168.1.1         //网关</span><br><span class="line">DNS1=192.168.1.1            //DNS</span><br><span class="line">DNS2=8.8.8.8                //备用DNS</span><br><span class="line">ONBOOT=yes                  //系统启动时启动此设置</span><br></pre></td></tr></table></figure>

<p>修改以后，使用命令重启网卡</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service network restart</span><br></pre></td></tr></table></figure>

<h3 id="配置映射"><a href="#配置映射" class="headerlink" title="配置映射"></a>配置映射</h3><p>修改文件 vi &#x2F;etc&#x2F;hosts<br>在文件最后添加映射地址，示例如下：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">192.168.1.101 node1</span><br><span class="line">192.168.1.102 node2</span><br><span class="line">192.168.1.103 node3</span><br></pre></td></tr></table></figure>
<p>配置好以后保存退出，输入命令：ping node1 ，可见实际 ping 的是 192.168.1.101。</p>
<h3 id="tcpdump"><a href="#tcpdump" class="headerlink" title="tcpdump"></a>tcpdump</h3><p>抓包分析工具<br><a target="_blank" rel="noopener" href="https://luyuhuang.tech/2022/12/05/tcpdump.html">使用tcpdump抓包</a></p>
<p>      </p>
<h2 id="查看系统信息"><a href="#查看系统信息" class="headerlink" title="查看系统信息"></a>查看系统信息</h2><hr>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">查看操作系统版本信息</span></span><br><span class="line">cat /proc/version</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">显示一些重要的系统信息，例如：内核名称、主机名、内核版本号、处理器类型之类信息</span></span><br><span class="line">uname -a</span><br></pre></td></tr></table></figure>

<h3 id="进程信息"><a href="#进程信息" class="headerlink" title="进程信息"></a>进程信息</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">查看所有正在运行的进程</span></span><br><span class="line">ps -ef</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">杀死pid进程</span></span><br><span class="line">kill pid</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">强制杀死该进程</span></span><br><span class="line">kill -9 pid</span><br></pre></td></tr></table></figure>

<h3 id="service"><a href="#service" class="headerlink" title="service"></a>service</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">service命令用于运行System V init脚本，这些脚本一般位于/etc/init.d 文件中</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">该命令可以直接运行脚本，而不需要加上路径</span></span><br><span class="line">service ssh status  #查看服务状态</span><br><span class="line">service --status-all  #查看所有服务状态</span><br><span class="line">service ssh restart  #重启服务</span><br></pre></td></tr></table></figure>

<h3 id="free"><a href="#free" class="headerlink" title="free"></a>free</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">这个命令用于显示系统当前内存的使用情况，包括已用内存、可用内存和交换内存的情况</span></span><br><span class="line">free -g #以g为单位输出内存的使用量，-g为GB，-m为MB，-k为KB，-b为字节</span><br><span class="line">free -t #查看所有内存的汇总</span><br></pre></td></tr></table></figure>

<h3 id="top"><a href="#top" class="headerlink" title="top"></a>top</h3><p>显示当前系统中占用资源最多的一些进程，shift+m 按照内存大小进行排序</p>
<h3 id="df"><a href="#df" class="headerlink" title="df"></a>df</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">显示文件系统的磁盘使用情况</span></span><br><span class="line">df -h</span><br></pre></td></tr></table></figure>

<h3 id="环境变量"><a href="#环境变量" class="headerlink" title="环境变量"></a>环境变量</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">查看当前环境变量</span></span><br><span class="line">echo $PATH</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">设置环境变量值</span></span><br><span class="line">export PATH=</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">添加一个路径</span></span><br><span class="line">export PATH=$PATH:newdir</span><br></pre></td></tr></table></figure>

<h3 id="watch"><a href="#watch" class="headerlink" title="watch"></a>watch</h3><p>用于动态查看命令执行的结果<br>比如，如果想要每隔一秒高亮显示 网络连接数的变化情况，则：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">watch -n 1 -d netstat -ant</span><br></pre></td></tr></table></figure>
<p>每隔一秒高亮显示 http 连接数的变化情况</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">watch -n 1 -d &#x27;pstree | grep  http&#x27;</span><br></pre></td></tr></table></figure>


<h3 id="vmstat"><a href="#vmstat" class="headerlink" title="vmstat"></a>vmstat</h3><p>常用系统性能分析工具，主要用来分析系统的内存使用情况，也常用来分析CPU上下文切换和中断的次数</p>
<h2 id="其它"><a href="#其它" class="headerlink" title="其它"></a>其它</h2><hr>
<h3 id="创建新用户"><a href="#创建新用户" class="headerlink" title="创建新用户"></a>创建新用户</h3><p>root权限下，使用 useradd 命令进行</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">useradd -m daidaini -c &quot;test creating a new user&quot;</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">指定密码</span></span><br><span class="line">passwd daidaini ***</span><br></pre></td></tr></table></figure>

<h3 id="nohup"><a href="#nohup" class="headerlink" title="nohup"></a>nohup</h3><p>no hang up 的缩写，不要挂起的意思，这个是常用的后台启动程序的方法。   </p>
<p>在交互环境下，我们可以直接将一些信息输出到当前界面；<br>那后台启动的程序，我们就会通常使用下边的命令，指定将信息输出到某个文件</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nohup command &gt; some.file 2&gt;&amp;1 &amp;</span><br></pre></td></tr></table></figure>
<p><strong>命令解释</strong></p>
<ul>
<li>1 表示标准输出</li>
<li>2 表示文件标准错误输出</li>
<li>2&gt;1&amp; 表示将两者合并，合并到的文件为some.file</li>
</ul>
<p><a target="_blank" rel="noopener" href="https://note.youdao.com/s/Eson3OZC">常见命令</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://daidaini.giteee.io/selfblog/2022/03/23/%E5%B8%B8%E7%94%A8Linux%E5%91%BD%E4%BB%A4/" data-id="cldmi24lv0009yhs1gbshhw5m" data-title="常用Linux命令" class="article-share-link">分享</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/selfblog/tags/%E7%AC%94%E8%AE%B0/" rel="tag">笔记</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-《Linux多线程服务端编程》阅读笔记" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/selfblog/2022/03/23/%E3%80%8ALinux%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%9C%8D%E5%8A%A1%E7%AB%AF%E7%BC%96%E7%A8%8B%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/" class="article-date">
  <time class="dt-published" datetime="2022-03-23T12:50:22.000Z" itemprop="datePublished">2022-03-23</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/selfblog/categories/C-%E7%BC%96%E7%A8%8B/">C++编程</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/selfblog/2022/03/23/%E3%80%8ALinux%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%9C%8D%E5%8A%A1%E7%AB%AF%E7%BC%96%E7%A8%8B%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/">Linux多线程服务端编程</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <p>1 当一个对象能被多个线程同时看到时，那么这个对象的销毁时机就会变得模糊不清，可能出现多种竞态条件：</p>
<ul>
<li>在即将析构一个对象时，从何而知此刻是否有其他线程正在执行该对象的成员函数</li>
<li>如何保证在执行成员函数期间，对象不会在另一个线程被析构</li>
<li>在调用某个对象的成员函数之前，如何得知这个对象还活着？它的析构函数会不会碰巧执行到一半？</li>
</ul>
<p>解决这些race condtion是C++多线程编程面临的基本问题。可以使用shared_ptr来一劳永逸地解决这些问题。</p>
<p>2 对象构造要做到线程安全，唯一的要求是构造期间不能泄漏this指针。</p>
<p>基于此，二段式构造–即构造函数 + initialize() – 有时会是好办法。这种方式虽然不符合C++教条，但是多线程下别无选择。</p>
<p>3 mutex不能安全地保护析构，因为一旦执行析构，mutex对象也会被销毁。这样，在多线程的情况下，如果其他线程正在使用mutex用到一半，就会有问题。</p>
<p>4 空悬指针：</p>
<p>两个指针p1，p2，指向堆里的同一个对象Object，并且p1和p2位于不同的线程中（线程A和线程B）。假设线程A通过p1指针将对象销毁了，那么p2就成了悬空指针。这是一种典型的C&#x2F;C++内存错误。</p>
<p>要想安全地销毁对象，最好在别人(即别的线程)都看不到的情况下，偷偷地做。这个也正是垃圾回收(gc)的原理，所有人用不到的一定是垃圾。</p>
<p>（悬空指针，是指指向的内容已经被释放的指针）</p>
<p>5 C++里可能出现的内存问题大致有如下几个方面：</p>
<ul>
<li>缓冲区溢出 (buffer overrun)</li>
<li>空悬指针 &#x2F; 野指针</li>
<li>重复释放 (double delete)</li>
<li>内存泄漏 (memory leak)</li>
<li>不配对的 new[] &#x2F; delete</li>
<li>内存碎片 (memory fragment)</li>
</ul>
<p>而正确地使用智能指针可以很轻易地解决前5种问题</p>
<p>6 shared_ptr的拷贝开销要比原始指针的拷贝开销要高（因为拷贝的时候需要修改引用计数，而修改引用计数需要加锁操作）。所以，我们在将shared_ptr作为函数参数传递的时候，尽量使用常引用的形式，这样减少拷贝次数，来减少性能损失。</p>
<p>7 让this指针，能变身为shared_ptr的方法，是让类继承 enable_shared_from_this。</p>
<p>8 弱回调：如果对象还活着，就调用它的成员函数，否则忽略之。</p>
<p>9 read-copy-update</p>
<p>10 不推荐使用信用量(Semaphore)，原因：</p>
<ul>
<li>条件变量配合互斥量可以完全替代其功能，而且更不易出错</li>
<li>semaphore has no notion of ownership</li>
<li>信号量有自己的计数值，而通常我们自己的数据结构也有长度值，这就造成了同样的信息存放了两份，需要时刻保持一致，这增加了程序员的负担和出错的可能。</li>
<li>如果要控制并发度，可以考虑用muduo::ThreadPool</li>
</ul>
<p>11 使用 pthread_once 来实现 Singleton</p>
<p>12 在”non-blocing IO + IO multiplexing” 这种模型（即Reactor模式）中，程序的基本结构是一个事件循环(event loop)， 以事件驱动(event-driven) 和事件回调的方式实现业务逻辑。</p>
<p>Reactor摸型的优点：</p>
<p>编程不难，效率也不错。不仅可以用于读写socket，连接的建立，甚至DNS解析都可以用非阻塞的方式进行，以提高并发度和吞吐量，对于IO密集的应用是一个不错的选择。</p>
<p>缺点：</p>
<p>它要求事件回调函数必须是非阻塞的。对于涉及网络IO的请求响应式协议，它容易割裂业务逻辑，使其散布于多个回调函数之中，相对不容易理解和维护。</p>
<p>13 one loop per thread</p>
<p>libev的作者说：</p>
<p>One loop per thread is usually a good model. Doing this is almost never wrong, sometimes a better-performance model exists, but it is always a good start.</p>
<p>这种方式的好处，在于：</p>
<ul>
<li>线程数目基本固定，可以在程序启动的时候设置，不会频繁创建与销毁</li>
<li>可以很方便地在线程之间调配负载</li>
<li>IO事件发生的线程是固定的，同一个TCP连接不必考虑事件并发</li>
</ul>
<p>Eventloop代表了线程的主循环，需要让哪个线程干活，就把timer或IOchannel(如TCP连接)注册到哪个线程的loop里即可。</p>
<p>对实时性有要求的额connection 可以单独用一个线程；</p>
<p>数据量大的connection可以独占一个线程，并把数据处理任务分摊到另几个计算线程中（用线程池）；</p>
<p>其他次要的辅助性connection可以共享一个线程。</p>
<p>对于具有一定规模的服务端程序，一般就会采用 non-blockong + IO multiplexing， 每个connection&#x2F;acceptor 都会注册到某个eventloop上，程序里有多个event loop,每个线程至多有一个event loop。</p>
<p>多线程程序对event loop 提出了更高的要求，那就是“线程安全”。要允许一个线程往别的线程的loop里塞数据，这个loop必须得是线程安全的。</p>
<p>14 进程间通信首选Sockets(主要是指TCP)，其最大的好处在于：可以跨主机，具有伸缩性。其他优势：</p>
<ul>
<li>在编程上，TCP sockets和pipe都是操作文件描述符，用来收发字节流，都可以 read&#x2F;write&#x2F;fcntl&#x2F;poll等。不同的是，TCP是双向的；Linux的pipe是单向的，使用没有TCP方便</li>
<li>TCP的port是由一个进程独占的，而且操作系会自动回收(listening port 和已建立连接的TCP socket都是文件描述符，在进程结束时操作系统会自动关闭所有文件描述符)。这说明，即使程序意外退出，也不会给系统留下垃圾，程序重启之后就可以比较容易地恢复，而不需要重启操作系统。还有一个好处，既然port是独占的，就可以防止程序重复启动。</li>
<li>两个进程通过TCP通信，如果一个崩溃了，操作系统会关闭连接，另一个进程就会立刻感知到，可以快速failover（故障转移）。</li>
<li>与其他IPC相比，TCP的一个天生的好处是“可记录、可重现”。tcpdump和Wireshark 是解决两个进程间协议和状态争端的好帮手，也是性能(吞吐量、延迟等)分析的利器。我们可以借此编写分布式程序的自动化回归测试。还可以用tcpcopy之类的工具进行压力测试。</li>
<li>TCP还能跨语言，服务端和客户端之间可以不必使用同一种语言</li>
<li>使用TCP这种字节流方式通信，会有 marshal&#x2F;unmarshal的开销，这就要求我们选用合适的消息格式，准确的说是 wire format(字节序列？)，推荐用 Google Protocol Buffers</li>
</ul>
<p>15 使用TCP长连接的好处有两点：</p>
<ul>
<li>容易定位分布式系统中的服务之间的依赖关系。只要在机器上运行 netstat -tpna | grep :port 就能立刻列出用到某服务的客户端地址，然后在客户端的机器上用 netstat 或者 lsof 命令找出是哪个进程发起的连接。</li>
<li>通过接收和发送队列的长度也比较容易定位网络或者程序故障。</li>
</ul>
<p>16 本书对 “服务器开发” 的定义，用一句话形容：</p>
<p>跑在多核机器上的Linux用户态的没有用户界面的长期运行的 网络应用程序，通常是分布式系统的组成部件。</p>
<p>17 多线程的适用场景时：提高响应速度，让IO和“计算”相互重叠，降低latency(延迟)。虽然多线程不能提高绝对性能，但是能提高平均响应性能。</p>
<p>一个程序要做成多线程的，大致要满足：</p>
<ul>
<li>有多个CPU可用。单核机器上多线程没有性能优势(但或许能简化并发业务逻辑的实现)</li>
<li>线程间有共享数据，即内存中的全局状态。</li>
<li>共享的数据是可以修改的，而不是静态常量表。</li>
<li>提供非均质的服务。即，事件的响应有优先级差异，我们可以用专门的线程来处理优先级高的事件。防止优先级反转。</li>
<li>latency 和 throuthput 同样重要，不是逻辑简单的 IO密集或是 CPU密集。换言之，程序是有相当的计算量的</li>
<li>能scale up(有规模的增长)。 一个好的多线程程序应该能享受增加CPU数目带来的好处，一旦CPU从8核升级到16核，程序能体现出这种升级带来的性能提升</li>
<li>具有可预测的性能。随着负载增加，性能缓慢下降，超过某个临界点之后会极速下降。线程数目一般不随负载变化。</li>
<li>多线程能有效地划分责任与功能，让每个线程的逻辑比较简单，任务单一，便于编码。而不是所有逻辑塞到一个event loop中，导致不同类别的事件之间相互影响</li>
</ul>
<p>18 多线程服务程序中的线程大致可以分为三类：</p>
<ul>
<li>IO线程，这类线程的主循环是IO multiplexing，阻塞地等在 select&#x2F;poll&#x2F;epoll_wait 等系统调用上。这类线程也处理定时事件。当然，它的功能不光光是IO，有些简单的计算也可以放入其中，比如消息的编码或者解码等。</li>
<li>计算线程，这类线程的主循环是 blocingqueue， 阻塞地等在 conditionvariable上。这类线程一般位于 thread pool中。这种线程一般不涉及IO，一般要避免任何阻塞操作</li>
<li>第三方库所用的线程，比如 logging ，又比如database connection等</li>
</ul>
<p>19 Linux 能同时启动多少个线程？</p>
<p>对于32-bit Linux，一个进程的地址空间是4GB，其中用户态能访问的为3GB左右，而一个线程的默认栈大小是10 MB，简单计算，一个进程大约可以同时启动300个线程。</p>
<p>对于64-bit系统，线程数目可大大增加</p>
<p>20 尽管C++03标准没有明说标准库的线程安全性，但</p>
<ul>
<li>我们可以遵循一个基本原则：凡是非共享的对象都是彼此独立的，如果一个对象从始至终只被一个线程使用，那么它就是安全的。</li>
<li>另外一个事实标准是：共享的对象的read-only操作是安全的，前提是不能有并发的写操作。</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://daidaini.giteee.io/selfblog/2022/03/23/%E3%80%8ALinux%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%9C%8D%E5%8A%A1%E7%AB%AF%E7%BC%96%E7%A8%8B%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/" data-id="cldmi24ln0003yhs1e51v21ag" data-title="Linux多线程服务端编程" class="article-share-link">分享</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/selfblog/tags/%E7%AC%94%E8%AE%B0/" rel="tag">笔记</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-初始化列表" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/selfblog/2022/03/23/%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8/" class="article-date">
  <time class="dt-published" datetime="2022-03-23T12:50:22.000Z" itemprop="datePublished">2022-03-23</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/selfblog/categories/C-%E7%BC%96%E7%A8%8B/">C++编程</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/selfblog/2022/03/23/%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8/">初始化列表</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h2 id="初始化列表的概念及使用"><a href="#初始化列表的概念及使用" class="headerlink" title="初始化列表的概念及使用"></a>初始化列表的概念及使用</h2><p>C++98中，标准允许使用花括号”{}”对数组元素进行统一的集合初始值设定。比如：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">int arr[5] = &#123;0&#125;;</span><br><span class="line">int arr[] = &#123;1,2,3,4&#125;;</span><br></pre></td></tr></table></figure>

<p>C++11中，这种初始化的方法，被扩展到了集合（列表）中。</p>
<p>总结初始化方法如下：<br>1） 等号加上赋值表达式，如 int a &#x3D; 3+4；<br>2） 等号 加上花括号的 初始化列表， 如 int a &#x3D; {3+4};<br>3） 圆括号式的表达式列表（expression list）, 如 int a &#x3D; (3+4);<br>4） 花括号式的初始化列表 ， 如 int a{3+4}</p>
<p>其中，第3、4中方式也可用于获取堆内存 new操作符中，如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">int * i = new int(5);</span><br><span class="line">double *d = new double(1.5f);</span><br></pre></td></tr></table></figure>

<p>标准模板库中容器对初始化列表的支持源自<initializer_list>这个头文件中initialize_list的类模板的支持。<br>只需要包含这个头文件，并且声明一个以initialize_list<T>模板类为参数的构造函数，同样可以使得自定义的类使用列表初始化。</p>
<p>利用初始化列表，重载operator[]，operator&#x3D; 以及使用辅助的数组。例子如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">#include &lt;iostream&gt;</span><br><span class="line">#include &lt;vector&gt;</span><br><span class="line">#include &lt;initializer_list&gt;</span><br><span class="line">using namespace std;</span><br><span class="line"></span><br><span class="line">class Mydata&#123;</span><br><span class="line">public:</span><br><span class="line">    Mydata&amp; operator[](initializer_list&lt;int&gt; l)&#123;</span><br><span class="line">        for(auto i=l.begin();i!=l.end();++i)</span><br><span class="line">            idx.push_back(*i);</span><br><span class="line">       return *this;     </span><br><span class="line">    &#125;</span><br><span class="line">    Mydata&amp; operator=(int v)&#123;</span><br><span class="line">        if(idx.empty()!=true)</span><br><span class="line">        &#123;</span><br><span class="line">            for(auto i = idx.begin();i!=idx.end();++i)</span><br><span class="line">            &#123;</span><br><span class="line">                d.resize((*i&gt;d.size())?*i:d.size());</span><br><span class="line">                d[*i-1] = v;</span><br><span class="line">            &#125;</span><br><span class="line">            idx.clear();</span><br><span class="line">        &#125;</span><br><span class="line">        return *this;</span><br><span class="line">    &#125;</span><br><span class="line">    void print()&#123;</span><br><span class="line">        for(auto i=d.begin();i!=d.end();++i)</span><br><span class="line">            cout&lt;&lt;*i&lt;&lt;&quot; &quot;;</span><br><span class="line">        cout&lt;&lt;endl;    </span><br><span class="line">    &#125;</span><br><span class="line">private:</span><br><span class="line">    vector&lt;int&gt; idx;  //辅助数组，用于记录index</span><br><span class="line">    vector&lt;int&gt; d;    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">int main()&#123;</span><br><span class="line">    Mydata d;</span><br><span class="line">    d[&#123;2,3,5&#125;] = 7;   //将第2，3，5位设为7</span><br><span class="line">    d[&#123;1,4,5,8&#125;] = 4; //第1，4，5，8位设为4</span><br><span class="line">    d.print();  //4 7 7 4 7 0 0 4</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>此外，初始化列表还可以用于函数返回的情况，但是返回一个初始化列表，通常会导致构造一个临时变量</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">vector&lt;int&gt;Func()&#123;</span><br><span class="line">    return &#123;1,3&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="防止类型收窄"><a href="#防止类型收窄" class="headerlink" title="防止类型收窄"></a>防止类型收窄</h2><p>使用列表初始化还有一个最大优势是 可以防止类型收窄。</p>
<p>类型收窄 一般是指一些可以使得数据变化或者精度丢失的隐式类型转换。</p>
<p>可能导致 类型收窄的典型情况如下：</p>
<ol>
<li><p>以浮点数隐式地转换为整型 比如： int a &#x3D; 1.2</p>
</li>
<li><p>从高精度的浮点数转化为低精度的浮点数，比如： 从long double 隐式地转为 double。或者从double 转为 float。 这种精度降低，都可以视为类型收窄</p>
</li>
<li><p>从整型转为浮点数。如果整型数大到无法使用浮点数精确表达，也可以视为类型收窄</p>
</li>
<li><p>从整型，转为较低长度地整型。 比如：unsigned char &#x3D; 1024; 1024是不能被8位地unsigned char 容纳的</p>
</li>
</ol>
<p>使用初始化列表，是不能容许类型收窄的情况出现的。会编译通不过。</p>
<h2 id="使用自定义初始化列表"><a href="#使用自定义初始化列表" class="headerlink" title="使用自定义初始化列表"></a>使用自定义初始化列表</h2><p>示例：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">class Warriors</span><br><span class="line">&#123;</span><br><span class="line">public:</span><br><span class="line">    Warriors(const initializer_list&lt;string&gt;&amp; members)</span><br><span class="line">    &#123;</span><br><span class="line">        for (auto&amp; data : members)</span><br><span class="line">        &#123;</span><br><span class="line">            players.emplace_back(data);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    vector&lt;string&gt; players;</span><br><span class="line"></span><br><span class="line">    void print()</span><br><span class="line">    &#123;</span><br><span class="line">        for (auto itm : players)</span><br><span class="line">            std::cout &lt;&lt; itm &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://daidaini.giteee.io/selfblog/2022/03/23/%E5%88%9D%E5%A7%8B%E5%8C%96%E5%88%97%E8%A1%A8/" data-id="cldmi24lu0008yhs1a7fyepoi" data-title="初始化列表" class="article-share-link">分享</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/selfblog/tags/C-%E7%89%B9%E6%80%A7/" rel="tag">C++特性</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-进程与线程" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/selfblog/2021/09/10/%E8%BF%9B%E7%A8%8B%E4%B8%8E%E7%BA%BF%E7%A8%8B/" class="article-date">
  <time class="dt-published" datetime="2021-09-10T05:47:31.000Z" itemprop="datePublished">2021-09-10</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/selfblog/categories/Linux%E7%B3%BB%E7%BB%9F/">Linux系统</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/selfblog/2021/09/10/%E8%BF%9B%E7%A8%8B%E4%B8%8E%E7%BA%BF%E7%A8%8B/">进程与线程</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <p><strong>进程与线程的区别</strong></p>
<p>当我们把代码编译链接生成后的可执行文件，装载到内存中进行运行，这个运行中的实例就是进程。</p>
<p>同一个可执行文件，可以有多个进程实例。而且，每个进程之间的内存资源都是相互独立、互不影响的。</p>
<p>换句话解释就是，进程是操作系统进行资源调度和内存分配的基本单位。</p>
<p>这些进程独享的资源，包括有代码、打开的文件、堆、栈、存放全局变量的数据段 以及一些内核内部使用的数据，比如运行状态等。</p>
<p>现代的操作系统都是会并发处理多任务的，所以必然会同时处理多个进程，那就必然需要进行进程的切换。而切换进程，意味着，需要将进程独占的那些资源都进行切换。</p>
<p>那由于进程管理的资源较多，相对切换的成本开销就较大了。这也是引入线程的概念的原因，线程是进程的一个子任务，是用来作为操作系统进行任务调度的最小单位。</p>
<p>首先线程是相对进程更轻量级的存在，</p>
<p>单个进程的线程间共享的资源包括：</p>
<ul>
<li>代码段</li>
<li>全局数据段和静态数据段</li>
<li>栈区</li>
</ul>
<p>理论上，栈区是属于线程是有的。</p>
<p>但是实际上，不同线程的栈区并没有严格的隔离机制来进行保护。</p>
<p>因此，如果一个线程拿到来自另一个线程栈帧上的指针，那么该线程就可以改变另一个线程的栈区，也就是说，</p>
<p>这些线程可以任意膝盖本属于另一个线程中的变量。</p>
<ul>
<li>堆区</li>
<li>打开的文件描述符</li>
<li>命令行参数</li>
<li>信号处理函数</li>
<li>进程ID、进程组ID</li>
</ul>
<p>线程独占的资源有：（即线程上下文）</p>
<ul>
<li>线程的栈区   栈指针?(用来指向栈顶吗？)</li>
<li>程序计数器（一种寄存器）</li>
<li>函数运行使用的寄存器（保存部分局部变量之类）</li>
<li>TLS(Thread Local Storage)</li>
</ul>
<p>保存进程的信息的地方 称为PCB，进程控制块。</p>
<p>保存线程的信息的地方，就是TCB，线程控制块。</p>
<p>PCB存储的信息包括各种资源信息：内存地址空间信息、所有打开的文件、所有的I&#x2F;O设备信息，</p>
<p>此外，还有进程描述符（即进程的标识，进程id），进程的运行状态，对应的用户描述符，各种寄存器的信息，以及进程的优先级（操作系统用来进行调度的算法需要）等信息。</p>
<p>TCB就相对少一点，除了线程描述符，也会记录对应的进程描述符，还有栈、寄存器等相关信息。</p>
<p><strong>参考</strong><br><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/352707156">线程间到底共享了哪些进程资源 - 知乎 (zhihu.com)</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://daidaini.giteee.io/selfblog/2021/09/10/%E8%BF%9B%E7%A8%8B%E4%B8%8E%E7%BA%BF%E7%A8%8B/" data-id="cldmi24m2000iyhs18d362vfk" data-title="进程与线程" class="article-share-link">分享</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/selfblog/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/" rel="tag">操作系统</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-函数模板function的实验" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/selfblog/2021/07/30/%E5%87%BD%E6%95%B0%E6%A8%A1%E6%9D%BFfunction%E7%9A%84%E5%AE%9E%E9%AA%8C/" class="article-date">
  <time class="dt-published" datetime="2021-07-30T04:25:28.000Z" itemprop="datePublished">2021-07-30</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/selfblog/categories/C-%E7%BC%96%E7%A8%8B/">C++编程</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/selfblog/2021/07/30/%E5%87%BD%E6%95%B0%E6%A8%A1%E6%9D%BFfunction%E7%9A%84%E5%AE%9E%E9%AA%8C/">std::function的实验</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <p><strong>std::function使用的几种形式</strong></p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">inline</span> <span class="keyword">namespace</span> function_use</span><br><span class="line">&#123;</span><br><span class="line">	<span class="function"><span class="type">int</span> <span class="title">FuncB</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		<span class="keyword">return</span> x + y;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">struct</span> <span class="title class_">FuncC</span></span><br><span class="line">	&#123;</span><br><span class="line">		<span class="function"><span class="type">int</span> <span class="title">operator</span><span class="params">()</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span></span></span><br><span class="line"><span class="function">		</span>&#123;</span><br><span class="line">			<span class="keyword">return</span> x + y;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="type">void</span> <span class="title">test_use_function</span><span class="params">()</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		<span class="keyword">using</span> funcType = std::function&lt;<span class="built_in">int</span>(<span class="type">int</span>, <span class="type">int</span>)&gt;;</span><br><span class="line"></span><br><span class="line">		funcType a = [](<span class="type">int</span> x, <span class="type">int</span> y) &#123;</span><br><span class="line">			<span class="keyword">return</span> x + y;</span><br><span class="line">		&#125;;</span><br><span class="line"></span><br><span class="line">		funcType b = FuncB;</span><br><span class="line"></span><br><span class="line">		funcType c = <span class="built_in">FuncC</span>();</span><br><span class="line"></span><br><span class="line">		cout &lt;&lt; <span class="string">&quot;a = &quot;</span> &lt;&lt; <span class="built_in">a</span>(<span class="number">3</span>, <span class="number">4</span>) &lt;&lt; endl;</span><br><span class="line">		cout &lt;&lt; <span class="string">&quot;b = &quot;</span> &lt;&lt; <span class="built_in">b</span>(<span class="number">30</span>, <span class="number">40</span>) &lt;&lt; endl;</span><br><span class="line">		cout &lt;&lt; <span class="string">&quot;c = &quot;</span> &lt;&lt; <span class="built_in">c</span>(<span class="number">30</span>, <span class="number">4</span>) &lt;&lt; endl;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>结果如下</strong></p>
<blockquote>
<p>a &#x3D; 7<br>b &#x3D; 70<br>c &#x3D; 34</p>
</blockquote>
<hr>
<p>增加变化，加一个类如下</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">FuncContainer</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="function"><span class="type">void</span> <span class="title">Push</span><span class="params">(funcType&amp;&amp; func)</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		cout &lt;&lt; <span class="string">&quot;using void Push(funcType&amp;&amp; func)\n&quot;</span>;</span><br><span class="line">		m_contianer.<span class="built_in">emplace_back</span>(std::forward&lt;funcType&gt;(func));</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="type">void</span> <span class="title">Push</span><span class="params">(funcType&amp; func)</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		cout &lt;&lt; <span class="string">&quot;using void Push(funcType&amp; func)\n&quot;</span>;</span><br><span class="line">		m_contianer.<span class="built_in">emplace_back</span>(func);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function">funcType&amp; <span class="title">Take</span><span class="params">(<span class="type">int</span> index)</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		cout &lt;&lt; <span class="string">&quot;using funcType&amp; Take(int index)\n&quot;</span>;</span><br><span class="line">		<span class="keyword">return</span> m_contianer[index];</span><br><span class="line">	&#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">	vector&lt;funcType&gt; m_contianer;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<p><strong>测试方法如下</strong></p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">test_use_function</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	FuncContainer container;</span><br><span class="line">	container.<span class="built_in">Push</span>([](<span class="type">int</span> x, <span class="type">int</span> y) &#123;</span><br><span class="line">		<span class="keyword">return</span> x + y;</span><br><span class="line">		&#125;);</span><br><span class="line">	container.<span class="built_in">Push</span>(FuncB);</span><br><span class="line">	container.<span class="built_in">Push</span>(<span class="built_in">FuncC</span>());</span><br><span class="line">	<span class="keyword">auto</span> afunc = container.<span class="built_in">Take</span>(<span class="number">0</span>);</span><br><span class="line">	<span class="keyword">auto</span> bfunc = container.<span class="built_in">Take</span>(<span class="number">1</span>);</span><br><span class="line">	<span class="keyword">auto</span> cfunc = container.<span class="built_in">Take</span>(<span class="number">2</span>);</span><br><span class="line">	cout &lt;&lt; <span class="string">&quot;a = &quot;</span> &lt;&lt; <span class="built_in">afunc</span>(<span class="number">3</span>, <span class="number">4</span>) &lt;&lt; endl;</span><br><span class="line">	cout &lt;&lt; <span class="string">&quot;b = &quot;</span> &lt;&lt; <span class="built_in">bfunc</span>(<span class="number">30</span>, <span class="number">40</span>) &lt;&lt; endl;</span><br><span class="line">	cout &lt;&lt; <span class="string">&quot;c = &quot;</span> &lt;&lt; <span class="built_in">cfunc</span>(<span class="number">30</span>, <span class="number">4</span>) &lt;&lt; endl;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>测试结果</strong></p>
<blockquote>
<p>using void Push(funcType&amp;&amp; func)<br>using void Push(funcType&amp;&amp; func)<br>using void Push(funcType&amp;&amp; func)<br>using funcType&amp; Take(int index)<br>using funcType&amp; Take(int index)<br>using funcType&amp; Take(int index)<br>a &#x3D; 7<br>b &#x3D; 70<br>c &#x3D; 34  </p>
</blockquote>
<p><strong>实验结论</strong></p>
<ol>
<li>std::function 作为通用的多态函数封装器， std::function 的实例能存储、复制及调用任何可调用 (Callable) 目标——函数、 lambda 表达式、 bind 表达式或其他函数对象，还有指向成员函数指针和指向数据成员指针</li>
<li>std::function 配合using （或者typedef），可以用来作为容器的元素进行存储</li>
<li>std::function 作为函数参数传递时，都是作为右值引用来进行传递的</li>
<li>std::function 重载的 operator&#x3D; 返回的是引用类型，所以，需要注意其存储元素的生命周期</li>
</ol>
<p><strong>参考:</strong></p>
<p><a target="_blank" rel="noopener" href="https://www.apiref.com/cpp-zh/cpp/utility/functional/function.html">std::function - C++中文 - API参考文档 (apiref.com)</a></p>
<p><a target="_blank" rel="noopener" href="http://note.youdao.com/noteshare?id=06b5f1b1fb45fa8bc1e478a1abf478eb&sub=978CCDA963334F62AAA7713154779E6E">http://note.youdao.com/noteshare?id=06b5f1b1fb45fa8bc1e478a1abf478eb&amp;sub=978CCDA963334F62AAA7713154779E6E</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://daidaini.giteee.io/selfblog/2021/07/30/%E5%87%BD%E6%95%B0%E6%A8%A1%E6%9D%BFfunction%E7%9A%84%E5%AE%9E%E9%AA%8C/" data-id="cldmi24ls0007yhs1cmuegyu8" data-title="std::function的实验" class="article-share-link">分享</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/selfblog/tags/C-%E6%A0%87%E5%87%86%E5%BA%93/" rel="tag">C++标准库</a></li></ul>

    </footer>
  </div>
  
</article>



  
    <article id="post-C-11线程" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/selfblog/2021/07/30/C-11%E7%BA%BF%E7%A8%8B/" class="article-date">
  <time class="dt-published" datetime="2021-07-30T01:59:29.000Z" itemprop="datePublished">2021-07-30</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/selfblog/categories/C-%E7%BC%96%E7%A8%8B/">C++编程</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/selfblog/2021/07/30/C-11%E7%BA%BF%E7%A8%8B/">C++标准线程库的使用</a>
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h2 id="引子"><a href="#引子" class="headerlink" title="引子"></a>引子</h2><p>c++11后新增了线程库，最主要最熟知的应该就是 std::thread，以及配合以线程同步用的std::mutex和std::conditon_variable。</p>
<p>对于多核环境的高性能并发处理，后端开发最需要熟知的就是线程池。<br>线程池本质上就是提前建立好多个线程，在需要使用的时候唤醒一个来处理，不需要的时候让其睡眠不占用资源。</p>
<p>以下先介绍标准线程库的一些使用方法，然后再依次实现下线程池。</p>
<hr>
<h2 id="使用std-thread库"><a href="#使用std-thread库" class="headerlink" title="使用std::thread库"></a>使用std::thread库</h2><h3 id="基本使用方式"><a href="#基本使用方式" class="headerlink" title="基本使用方式"></a>基本使用方式</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">ThreadFunc</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="comment">//do something</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function">std::thread <span class="title">t</span><span class="params">(ThreadFunc)</span></span>;</span><br></pre></td></tr></table></figure>

<p>如果是带参数的线程函数，就可以：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">ThreadFunc</span><span class="params">(<span class="type">int</span> count)</span></span>&#123;</span><br><span class="line">    <span class="comment">//do something</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function">std::thread <span class="title">t</span><span class="params">(ThreadFunc, <span class="number">5</span>)</span></span>;</span><br><span class="line"><span class="comment">//或者 使用&lt;functional&gt;中提供的 std::bind方法，将参数绑定到对应的线程函数上</span></span><br><span class="line"><span class="function">std::thread <span class="title">t</span><span class="params">(std::bind(ThreadFunc, <span class="number">5</span>))</span></span>;</span><br></pre></td></tr></table></figure>
<h3 id="关于线程的结束"><a href="#关于线程的结束" class="headerlink" title="关于线程的结束"></a>关于线程的结束</h3><p>线程启动后，一般需要等到线程函数运行完，线程才能结束</p>
<p>标准库对于线程的结束也有两种方式：</p>
<p>1） 在主线程中调用join方法，主线程就会阻塞等待到线程函数运行完，然后结束</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(t.<span class="built_in">joinable</span>())</span><br><span class="line">    t.<span class="built_in">join</span>()</span><br></pre></td></tr></table></figure>

<p>2）调用detach方法，将线程从主线程分离。这种形式，主线程不会被阻塞，也不会知道分离出去的线程什么时候结束。</p>
<p>如果确定主线程肯定比线程函数晚结束，或者线程和主线程的存活时间是一致的，则可以直接在启动线程后，直接detach。</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">t.<span class="built_in">detach</span>();</span><br><span class="line"><span class="comment">//简便的写法，声明和detach一起调用</span></span><br><span class="line">std::<span class="built_in">thread</span>(ThreadFunc).<span class="built_in">detach</span>();</span><br></pre></td></tr></table></figure>

<h3 id="std-thread启动线程的其他方式"><a href="#std-thread启动线程的其他方式" class="headerlink" title="std::thread启动线程的其他方式"></a>std::thread启动线程的其他方式</h3><p>std::thread启动线程，也可以使用lambda表达式，参考如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">std::<span class="built_in">thread</span>([]()&#123;</span><br><span class="line">			<span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">20</span>; ++i)</span><br><span class="line">			&#123;</span><br><span class="line">				cout &lt;&lt; <span class="string">&#x27;[&#x27;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;]:&quot;</span> &lt;&lt; <span class="built_in">static_cast</span>&lt;<span class="type">char</span>&gt;(<span class="string">&#x27;A&#x27;</span> + i) &lt;&lt; endl;</span><br><span class="line">				this_thread::<span class="built_in">sleep_for</span>(<span class="number">10</span>ms);</span><br><span class="line">			&#125;&#125;</span><br><span class="line">           );</span><br></pre></td></tr></table></figure>


<p>对于将参数为引用的函数来作为线程函数，也有如下两种操作可参考：</p>
<p>1） 使用lambda表达式，如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">unordered_map&lt;<span class="type">int</span>, string&gt; myMapSrc&#123;</span><br><span class="line">	&#123;<span class="number">1</span>, <span class="string">&quot;first&quot;</span> &#125;,</span><br><span class="line">	&#123;<span class="number">2</span>, <span class="string">&quot;second&quot;</span>&#125;,</span><br><span class="line">	&#123;<span class="number">3</span>, <span class="string">&quot;third&quot;</span>&#125;,</span><br><span class="line">	&#123;<span class="number">4</span>, <span class="string">&quot;four&quot;</span>&#125;,</span><br><span class="line">	&#123;<span class="number">5</span>, <span class="string">&quot;five&quot;</span>&#125;</span><br><span class="line">	&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//捕获列表使用 &amp; 就可以</span></span><br><span class="line">std::<span class="built_in">thread</span>([&amp;myMapSrc]()&#123;</span><br><span class="line">			<span class="keyword">for</span> (<span class="type">const</span> <span class="keyword">auto</span>&amp; item : myMapSrc)</span><br><span class="line">			&#123;</span><br><span class="line">				cout &lt;&lt; <span class="string">&#x27;[&#x27;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;]:&quot;</span> &lt;&lt; item.first &lt;&lt; <span class="string">&quot; = &quot;</span> &lt;&lt; item.second &lt;&lt; endl;</span><br><span class="line">			&#125;&#125;</span><br><span class="line">	).<span class="built_in">detach</span>();</span><br></pre></td></tr></table></figure>

<p>2） 使用std::bind，如下</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">ThreadFunc</span><span class="params">(unordered_map&lt;<span class="type">int</span>, string&gt;&amp; refRec)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">for</span> (<span class="type">const</span> <span class="keyword">auto</span>&amp; item : refSrc)</span><br><span class="line">	&#123;</span><br><span class="line">		cout &lt;&lt; <span class="string">&#x27;[&#x27;</span> &lt;&lt; this_thread::<span class="built_in">get_id</span>() &lt;&lt; <span class="string">&quot;]:&quot;</span> &lt;&lt; item.first &lt;&lt; <span class="string">&quot; = &quot;</span> &lt;&lt; item.second &lt;&lt; endl;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line">std::<span class="built_in">thread</span>(std::<span class="built_in">bind</span>(ThreadFunc, std::<span class="built_in">ref</span>(myMapSrc)));</span><br></pre></td></tr></table></figure>

<p>由于std::bind总是使用值拷贝的形式传参，哪怕函数声明为引用，std::bind传递的时候也是值传递。所以，标准库提供了std::ref来给std::bind传引用。</p>
<h3 id="std-async"><a href="#std-async" class="headerlink" title="std::async"></a>std::async</h3><p>标准库，在std::thread的基础上，封装了一些方法，有std::promise，std::pacakged_task，以及std::async。</p>
<p>这其中，std::promise以及std::pacakged_task的使用，一般还是要配合std::thread以及std::future来。</p>
<p>这边，比较推荐使用std::async来启动一个线程来执行一个异步任务，简单的示例代码如下：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">std::<span class="built_in">async</span>(launch::async, ThreadFunc);</span><br><span class="line"><span class="comment">//带参数，则</span></span><br><span class="line">std::<span class="built_in">async</span>(launch::async, ThreadFunc, param1, param2...);</span><br></pre></td></tr></table></figure>

<p>std::async 方法有返回值，类型是std::future<T></p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">string <span class="title">ThreadFuncStr</span><span class="params">(<span class="type">int</span> data)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    this_thread::<span class="built_in">sleep_for</span>(<span class="number">1</span>s);</span><br><span class="line">	cout &lt;&lt; <span class="string">&quot;This is ThreadFuncStr\n&quot;</span>;</span><br><span class="line">	<span class="keyword">return</span> <span class="built_in">to_string</span>(data);</span><br><span class="line">&#125;</span><br><span class="line">std::future&lt;string&gt; result = std::<span class="built_in">async</span>(launch::async, ThreadFuncStr, <span class="number">100</span>);</span><br><span class="line"><span class="comment">//do some other thing </span></span><br><span class="line">string str = result.<span class="built_in">get</span>();</span><br><span class="line">cout &lt;&lt; str &lt;&lt;endl;</span><br></pre></td></tr></table></figure>

<p><strong>说明：</strong></p>
<p>std::async， 如果第一个参数是launch::async，那就是立即启动线程任务，但是线程启动后，不会阻塞当前线程。</p>
<p>只有在后续调用reuslt.get()的时候，会阻塞，直到线程函数返回需要的结果。</p>
<p><strong>使用建议：</strong></p>
<p>推荐std::async，是因为它将thread的概念隐藏到了底层，方法本身就成为了，我就是异步去执行一个任务。</p>
<p>所以，如果我们一旦碰到需要读写文件或者网络请求这种涉及IO，耗时不确定可能会阻塞当前运行线程的时候，都可以调用async，来启动一个异步任务完成这部分的业务处理。  </p>
<p><strong>另外：</strong></p>
<p>如果想使用std::thread的形式，也需要获取线程函数执行后的返回值，可以使用packaged_task，简单举例：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="function">std::packaged_task&lt;<span class="title">string</span><span class="params">(<span class="type">int</span>)</span>&gt; <span class="title">task</span><span class="params">(ThreadFuncStr)</span></span>;	</span><br><span class="line">    std::future&lt;string&gt; async_result = task.<span class="built_in">get_future</span>();	</span><br><span class="line">    std::<span class="built_in">thread</span>(std::<span class="built_in">move</span>(task), <span class="number">100</span>).<span class="built_in">detach</span>();	</span><br><span class="line">    cout &lt;&lt; async_result.<span class="built_in">get</span>() &lt;&lt; endl;  	</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>感觉用起来，不如std::async方便，感兴趣的可以自行研究(报考std::promise 也是，它们都有各自的应用场景)。  </p>
<hr>
<h2 id="线程池"><a href="#线程池" class="headerlink" title="线程池"></a>线程池</h2><p>有执行一个异步任务来获取结果的需求，肯定也有不定时执行多个异步任务的需求。</p>
<p>最典型的就是，服务端处理多个不同用户的业务逻辑的场景：</p>
<p>1）每个用户的业务处理，一般都需要在自己独立的线程中运行；</p>
<p>2）线程处理完一个用户的业务逻辑，还可以处理另一个用户的业务逻辑。</p>
<p>这种情况，就比较适合用线程池了。</p>
<p>下边就是用C++11标准库实现的线程池的代码：</p>
<figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">FixedThreadPool</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">	<span class="keyword">using</span> FuncTaskType = std::function&lt;<span class="built_in">void</span>()&gt;;</span><br><span class="line"></span><br><span class="line">	<span class="built_in">FixedThreadPool</span>(<span class="type">size_t</span> threadCount) :</span><br><span class="line">		<span class="built_in">m_ResData</span>(<span class="built_in">make_shared</span>&lt;ResInfo&gt;())</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; threadCount; ++i)</span><br><span class="line">		&#123;</span><br><span class="line">			std::<span class="built_in">thread</span>(std::<span class="built_in">bind</span>(&amp;FixedThreadPool::ThreadFunc, <span class="keyword">this</span>)).<span class="built_in">detach</span>();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	~<span class="built_in">FixedThreadPool</span>()</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="keyword">if</span> (m_ResData != <span class="literal">nullptr</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			<span class="function">std::lock_guard&lt;std::mutex&gt; <span class="title">guard</span><span class="params">(m_ResData-&gt;Mtx)</span></span>;</span><br><span class="line">			m_ResData-&gt;IsShutdown = <span class="literal">true</span>;</span><br><span class="line">		&#125;</span><br><span class="line">		m_ResData-&gt;Cv.<span class="built_in">notify_all</span>();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="type">void</span> <span class="title">Execute</span><span class="params">(FuncTaskType&amp;&amp; task)</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		<span class="function">lock_guard&lt;mutex&gt; <span class="title">guard</span><span class="params">(m_ResData-&gt;Mtx)</span></span>;</span><br><span class="line">		m_ResData-&gt;Tasks.<span class="built_in">emplace</span>(std::forward&lt;FuncTaskType&gt;(task));</span><br><span class="line">		m_ResData-&gt;Cv.<span class="built_in">notify_one</span>();</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">	<span class="function"><span class="type">void</span> <span class="title">ThreadFunc</span><span class="params">()</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		<span class="function">unique_lock&lt;mutex&gt; <span class="title">lk</span><span class="params">(m_ResData-&gt;Mtx)</span></span>;</span><br><span class="line">		<span class="keyword">do</span></span><br><span class="line">		&#123;</span><br><span class="line">			<span class="keyword">if</span> (!m_ResData-&gt;Tasks.<span class="built_in">empty</span>())</span><br><span class="line">			&#123;</span><br><span class="line">				<span class="keyword">auto</span> currentTask = std::<span class="built_in">move</span>(m_ResData-&gt;Tasks.<span class="built_in">front</span>());</span><br><span class="line">				m_ResData-&gt;Tasks.<span class="built_in">pop</span>();</span><br><span class="line">				lk.<span class="built_in">unlock</span>();</span><br><span class="line">				<span class="built_in">currentTask</span>();</span><br><span class="line">				lk.<span class="built_in">lock</span>();</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">else</span> <span class="keyword">if</span> (m_ResData-&gt;IsShutdown)</span><br><span class="line">			&#123;</span><br><span class="line">				<span class="keyword">break</span>;</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">else</span></span><br><span class="line">			&#123;</span><br><span class="line">				m_ResData-&gt;Cv.<span class="built_in">wait</span>(lk);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125; <span class="keyword">while</span> (<span class="literal">true</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">struct</span> <span class="title class_">ResInfo</span></span><br><span class="line">	&#123;</span><br><span class="line">		mutex Mtx;</span><br><span class="line">		condition_variable Cv;</span><br><span class="line">		<span class="type">bool</span> IsShutdown = <span class="literal">false</span>;</span><br><span class="line">		<span class="comment">//线程函数任务队列</span></span><br><span class="line">		queue&lt;FuncTaskType&gt; Tasks;</span><br><span class="line">	&#125;;</span><br><span class="line"></span><br><span class="line">	std::shared_ptr&lt;ResInfo&gt; m_ResData;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p><strong>简单解释：</strong></p>
<ul>
<li><p>ResInfo 是需要的一些信息，包含线程同步用互斥量和条件变量，线程池的开关，以及存储线程任务的队列</p>
</li>
<li><p>ResInfo 使用 shared_ptr 是因为 每个线程都会进行一份拷贝</p>
</li>
<li><p>存储队列，使用queue，满足先进先出</p>
</li>
<li><p>线程任务，统一使用 函数模板 std::function&lt;void()&gt; ，这样，后续再配合std::bind，就可以执行所有带参数和不带参数的线程函数</p>
</li>
<li><p>构造的时候，直接启动对应数量的线程，每个线程的运行都封装在ThreadFunc</p>
</li>
<li><p>ThreadFunc 使用unique_lock 一是需要配合条件变量进行wait，</p>
<p>二是，在将线程任务从队列中取出来之后，就不需要再锁了，可以unlock。</p>
<p>对于线程池内部的mutex，它用来保护的数据，其实就是线程任务队列，所以将线程任务从队列中取出来之后，这个锁的任务就达成了。</p>
<p>执行完线程任务，再锁住，是该线程在循环，执行完上一个任务，就会去队列中取下一个任务。</p>
</li>
<li><p>Execute方法，顾名思义，就是用来执行任务的。右值引用作为参数进行传递时，会转换成左值，需配合完美转发std::forward使用</p>
</li>
<li><p>最后，析构函数，主要是确保开关置为true，启动的线程函数可以正常运行结束。</p>
</li>
</ul>
<p><strong>参考:</strong></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/chenwh_cn/article/details/116492680">C++11 (三) - std::function、std::bind、std::ref</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_44862644/article/details/115765250">C++11 std::thread detach()与join()用法总结</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://daidaini.giteee.io/selfblog/2021/07/30/C-11%E7%BA%BF%E7%A8%8B/" data-id="cldmi24li0001yhs189xt4kap" data-title="C++标准线程库的使用" class="article-share-link">分享</a>
      
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/selfblog/tags/C-%E6%A0%87%E5%87%86%E5%BA%93/" rel="tag">C++标准库</a></li></ul>

    </footer>
  </div>
  
</article>



  


</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">分类</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/selfblog/categories/C-11/">C++11</a></li><li class="category-list-item"><a class="category-list-link" href="/selfblog/categories/C-%E7%BC%96%E7%A8%8B/">C++编程</a></li><li class="category-list-item"><a class="category-list-link" href="/selfblog/categories/Linux%E7%B3%BB%E7%BB%9F/">Linux系统</a></li><li class="category-list-item"><a class="category-list-link" href="/selfblog/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/">操作系统</a></li><li class="category-list-item"><a class="category-list-link" href="/selfblog/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/">数据结构与算法</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">标签</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/selfblog/tags/C-%E5%AD%A6%E4%B9%A0/" rel="tag">C++学习</a></li><li class="tag-list-item"><a class="tag-list-link" href="/selfblog/tags/C-%E6%A0%87%E5%87%86%E5%BA%93/" rel="tag">C++标准库</a></li><li class="tag-list-item"><a class="tag-list-link" href="/selfblog/tags/C-%E7%89%B9%E6%80%A7/" rel="tag">C++特性</a></li><li class="tag-list-item"><a class="tag-list-link" href="/selfblog/tags/C%E8%AF%AD%E8%A8%80/" rel="tag">C语言</a></li><li class="tag-list-item"><a class="tag-list-link" href="/selfblog/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/" rel="tag">操作系统</a></li><li class="tag-list-item"><a class="tag-list-link" href="/selfblog/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/" rel="tag">数据结构与算法</a></li><li class="tag-list-item"><a class="tag-list-link" href="/selfblog/tags/%E7%AC%94%E8%AE%B0/" rel="tag">笔记</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">标签云</h3>
    <div class="widget tagcloud">
      <a href="/selfblog/tags/C-%E5%AD%A6%E4%B9%A0/" style="font-size: 10px;">C++学习</a> <a href="/selfblog/tags/C-%E6%A0%87%E5%87%86%E5%BA%93/" style="font-size: 20px;">C++标准库</a> <a href="/selfblog/tags/C-%E7%89%B9%E6%80%A7/" style="font-size: 20px;">C++特性</a> <a href="/selfblog/tags/C%E8%AF%AD%E8%A8%80/" style="font-size: 10px;">C语言</a> <a href="/selfblog/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/" style="font-size: 10px;">操作系统</a> <a href="/selfblog/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/" style="font-size: 10px;">数据结构与算法</a> <a href="/selfblog/tags/%E7%AC%94%E8%AE%B0/" style="font-size: 20px;">笔记</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">归档</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/selfblog/archives/2023/02/">二月 2023</a></li><li class="archive-list-item"><a class="archive-list-link" href="/selfblog/archives/2023/01/">一月 2023</a></li><li class="archive-list-item"><a class="archive-list-link" href="/selfblog/archives/2022/03/">三月 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/selfblog/archives/2021/09/">九月 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/selfblog/archives/2021/07/">七月 2021</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">最新文章</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/selfblog/2023/02/09/%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E7%9A%84%E7%90%86%E8%A7%A3/">回调函数的理解</a>
          </li>
        
          <li>
            <a href="/selfblog/2023/02/07/%E4%BA%8C%E5%88%86%E6%B3%95%E7%9A%84%E7%90%86%E8%A7%A3/">二分法的理解</a>
          </li>
        
          <li>
            <a href="/selfblog/2023/01/31/%E6%8C%87%E9%92%88%E7%9A%84%E7%90%86%E8%A7%A3/">C指针的理解</a>
          </li>
        
          <li>
            <a href="/selfblog/2023/01/31/%E7%90%86%E8%A7%A3CPP%E7%9A%84%E7%A7%BB%E5%8A%A8%E8%AF%AD%E4%B9%89/">理解CPP的移动语义</a>
          </li>
        
          <li>
            <a href="/selfblog/2022/03/23/%E5%B8%B8%E7%94%A8Linux%E5%91%BD%E4%BB%A4/">常用Linux命令</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      
      &copy; 2023 Daidaini<br>
      Powered by <a href="https://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>

    </div>
    <nav id="mobile-nav">
  
    <a href="/selfblog/" class="mobile-nav-link">Home</a>
  
    <a href="/selfblog/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    


<script src="/selfblog/js/jquery-3.4.1.min.js"></script>



  
<script src="/selfblog/fancybox/jquery.fancybox.min.js"></script>




<script src="/selfblog/js/script.js"></script>





  </div>
</body>
</html>